Skip to content

Commit 144b17e

Browse files
committed
refactor(mosaic): share a kebabCase util for data attributes
1 parent 9268638 commit 144b17e

5 files changed

Lines changed: 34 additions & 4 deletions

File tree

‎packages/mosaic/AGENTS.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Most problems a feature hits already have a shared answer. Find the row for your
4646
| A spinner that should not flash or flicker | `useSpinDelay` (`src/hooks/`) |
4747
| A placeholder for content that is still loading | `Skeleton` (`src/components/skeleton/`), see [Skeletons](docs/skeletons.md) |
4848
| A date shown to the user | `formatDate` (`src/utils/format-date.ts`) |
49+
| A value turned into kebab case, e.g. for a `data-*` attribute | `kebabCase` (`src/utils/kebab-case.ts`) |
4950
| Current time for display labels | `useNow` + `MosaicProvider` ([#10098](https://github.com/clerk/javascript/pull/10098), once available) |
5051
| Whether enterprise SSO permits adding another identification | `allowsIdentificationCreation` (`src/utils/allows-identification-creation.ts`) |
5152
| An inline message row that animates open and closed | `useHeldMessage`, `useMessageHeight`, `FeedbackBody` (`src/utils/feedback.tsx`) + `styles/feedback.styles.ts` |

‎packages/mosaic/src/features/user-profile/user-profile-provider-media.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as stylex from '@stylexjs/stylex';
33
import { IconFrame } from '../../components/icon';
44
import { Section } from '../../components/section';
55
import { mergeStyleProps, themeProps } from '../../props';
6+
import { kebabCase } from '../../utils/kebab-case';
67
import { styles } from './user-profile-provider-media.styles';
78

89
export function UserProfileProviderMedia({
@@ -17,7 +18,7 @@ export function UserProfileProviderMedia({
1718
monochromeIcon?: boolean;
1819
}) {
1920
const url = iconUrl?.trim();
20-
const slot = themeProps('provider-icon', { provider: iconId?.replaceAll('_', '-') });
21+
const slot = themeProps('provider-icon', { provider: iconId && kebabCase(iconId) });
2122
const offset = iconId === 'apple' && styles.appleOffset;
2223
return (
2324
<Section.Media size='lg'>

‎packages/mosaic/src/props.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type * as stylex from '@stylexjs/stylex';
22
import type React from 'react';
33

44
import type { ComponentProps } from './primitives/utils';
5+
import { kebabCase } from './utils/kebab-case';
56

67
/**
78
* What `xstyle` accepts: exactly what `stylex.props(...)` does, since that is where a part
@@ -63,8 +64,6 @@ export type MosaicComponentProps<Tag extends keyof React.JSX.IntrinsicElements>
6364

6465
type VariantValue = string | number | boolean | null | undefined;
6566

66-
const kebab = (name: string) => name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
67-
6867
/**
6968
* The stable class + variant reflection for a slot.
7069
*
@@ -84,7 +83,7 @@ export function themeProps(
8483
if (value == null || value === false) {
8584
continue;
8685
}
87-
attrs[`data-${kebab(axis)}`] = value === true ? '' : String(value);
86+
attrs[`data-${kebabCase(axis)}`] = value === true ? '' : String(value);
8887
}
8988
return { className: `cl-${slot}`, ...attrs };
9089
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { kebabCase } from '../kebab-case';
4+
5+
describe('kebabCase', () => {
6+
it.each([
7+
['okx_wallet', 'okx-wallet'],
8+
['colorScheme', 'color-scheme'],
9+
['HTTPRequest', 'http-request'],
10+
['some whitespace', 'some-whitespace'],
11+
['already-kebab', 'already-kebab'],
12+
['apple', 'apple'],
13+
['', ''],
14+
])('converts %j to %j', (input, expected) => {
15+
expect(kebabCase(input)).toBe(expected);
16+
});
17+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function kebabCase(value: string): string {
2+
return words(value)
3+
.map(word => word.toLowerCase())
4+
.join('-');
5+
}
6+
7+
const CASE_SPLIT_PATTERN =
8+
/\p{Lu}?\p{Ll}+|[0-9]+|\p{Lu}+(?!\p{Ll})|\p{Emoji_Presentation}|\p{Extended_Pictographic}|\p{L}+/gu;
9+
10+
function words(value: string): string[] {
11+
return Array.from(value.match(CASE_SPLIT_PATTERN) ?? []);
12+
}

0 commit comments

Comments
 (0)