@carto/meridian-ds 3.0.2-alpha.8166b14.239 → 3.0.2-alpha.82ee07e.240
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.0.2-alpha.
|
|
2
|
+
"version": "3.0.2-alpha.82ee07e.240",
|
|
3
3
|
"conventions": "# Meridian usage conventions (for code-generating agents)\n\nSystem-level rules an agent should apply across all components. These are the\ncross-cutting opinions that don't belong to any single component, so they live\nhere once instead of being copied into every component's metadata.\n\n## Choosing a component\n\n- Prefer a Meridian component over a bare MUI one whenever a Meridian wrapper\n exists — the wrappers carry CARTO theming, a11y defaults, and narrowed APIs.\n- Resolve \"which component\" by reading each one's `description` + `keywords`,\n then confirm with its `decisionTree` (the conditions that point elsewhere).\n Don't infer selection from prop lists.\n\n## Accessibility\n\n- Any icon-only control needs an accessible name: `tooltip` or `aria-label`.\n- Don't disable a control without telling the user why — pair a disabled action\n with a tooltip explaining the requirement.\n\n## Props\n\n- Exact props, types, and JSDoc come from the package's type declarations —\n `import` the component and let your IDE/tsc show the signature. Don't guess a\n prop name; the compiler validates it.\n- A component's curation lists the high-value inherited MUI props worth knowing\n (`curation.mui`, e.g. `color`/`variant`/`size`); the rest of MUI's surface is\n available but rarely needed.\n- Never pass `sx`/inline styles to restyle a component; use its documented props\n and the theme tokens.\n\n## Examples\n\n- Canonical, tested usage lives in the component's Storybook stories. Call\n `meridian_get_examples` for a component to get that source — never invent example\n code when a story exists.\n",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
@@ -92,16 +92,6 @@
|
|
|
92
92
|
"id": "autocomplete--prefix",
|
|
93
93
|
"code": "const options = {\n title: 'Components/Autocomplete/Autocomplete',\n component: Autocomplete,\n tags: ['validated', 'custom-component'],\n args: {\n required: false,\n disabled: false,\n error: false,\n readOnly: false,\n loading: false,\n disableCloseOnSelect: false,\n },\n argTypes: {\n variant: {\n control: 'select',\n options: ['outlined', 'filled'],\n },\n size: {\n control: 'select',\n options: ['small', 'medium'],\n },\n label: {\n control: 'text',\n },\n placeholder: {\n control: 'text',\n },\n showFilters: {\n table: { disable: true },\n },\n },\n parameters: {\n design: {\n type: 'figma',\n url: 'https://www.figma.com/file/nmaoLeo69xBJCHm9nc6lEV/C4R-Components?type=design&node-id=1534-26505',\n },\n },\n} satisfies Meta\n\nconst top100Films = getTop100Films()\n\nconst PrefixTemplate = ({\n label,\n variant,\n placeholder,\n helperText,\n error,\n size,\n required,\n disableCloseOnSelect,\n ...props\n}: AutocompleteProps<(typeof top100Films)[number]> &\n AutocompleteInputProps) => {\n return (\n <Autocomplete\n {...props}\n options={top100Films}\n getOptionLabel={(option) => option.title}\n renderInput={(params) => {\n params.InputProps.startAdornment = (\n <InputAdornment position='start'>\n {<AnalyticsOutlined />}\n </InputAdornment>\n )\n return (\n <TextField\n {...params}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n variant={variant}\n error={error}\n size={size}\n required={required}\n InputLabelProps={{ shrink: true }}\n />\n )\n }}\n size={size}\n disableCloseOnSelect={disableCloseOnSelect}\n />\n )\n}\n\nconst commonArgs = {\n label: 'Label text',\n placeholder: 'Placeholder text',\n helperText: 'Helper text.',\n}\n\nexport const Prefix = {\n render: PrefixTemplate,\n args: { ...commonArgs },\n}"
|
|
94
94
|
},
|
|
95
|
-
{
|
|
96
|
-
"name": "Multiple",
|
|
97
|
-
"id": "autocomplete--multiple",
|
|
98
|
-
"code": "const MultipleTemplate = () => (\n <DocContainer severity='warning'>\n <i>multiple</i> boolean is deprecated, use\n <DocLink href='https://github.com/CartoDB/meridian-ds/blob/main/src/components/Autocomplete/MultipleAutocomplete/MultipleAutocomplete.tsx'>\n MultipleAutocomplete.\n </DocLink>\n <Typography mt={2}>\n It uses <i>Mui Autocomplete</i> and extends it to add new checkbox to each\n item and allow filtering.\n </Typography>\n <Typography mt={2}>\n Import it from:\n <DocHighlight>components/Autocomplete/MultipleAutocomplete</DocHighlight>\n </Typography>\n <Typography mt={2}>\n For external use:\n <DocHighlight>\n {'import { MultipleAutocomplete } from \"@carto/meridian-ds\"'}\n </DocHighlight>\n </Typography>\n </DocContainer>\n)\n\nexport const Multiple = {\n render: MultipleTemplate,\n parameters: {\n ...documentationStoryParameters,\n },\n}"
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"name": "Creatable",
|
|
102
|
-
"id": "autocomplete--creatable",
|
|
103
|
-
"code": "const CreatableTemplate = () => (\n <DocContainer severity='warning'>\n Use\n <DocLink href='https://github.com/CartoDB/meridian-ds/blob/main/src/components/Autocomplete/CreatableAutocomplete/CreatableAutocomplete.tsx'>\n CreatableAutocomplete\n </DocLink>\n <Typography mt={2}>\n It uses <i>Mui Autocomplete</i> and extends it to add new MenuItem options\n that don"t exist yet.\n </Typography>\n <Typography mt={2}>\n Import it from:\n <DocHighlight component='span'>\n components/Autocomplete/CreatableAutocomplete\n </DocHighlight>\n </Typography>\n <Typography mt={2}>\n For external use:\n <DocHighlight component='span'>\n {'import { CreatableAutocomplete } from \"@carto/meridian-ds\"'}\n </DocHighlight>\n </Typography>\n </DocContainer>\n)\n\nexport const Creatable = {\n render: CreatableTemplate,\n parameters: {\n ...documentationStoryParameters,\n },\n}"
|
|
104
|
-
},
|
|
105
95
|
{
|
|
106
96
|
"name": "Medium",
|
|
107
97
|
"id": "autocomplete--medium",
|
|
@@ -136,11 +126,6 @@
|
|
|
136
126
|
"name": "GroupedNonVirtualized",
|
|
137
127
|
"id": "autocomplete--grouped-non-virtualized",
|
|
138
128
|
"code": "const options = {\n title: 'Components/Autocomplete/Autocomplete',\n component: Autocomplete,\n tags: ['validated', 'custom-component'],\n args: {\n required: false,\n disabled: false,\n error: false,\n readOnly: false,\n loading: false,\n disableCloseOnSelect: false,\n },\n argTypes: {\n variant: {\n control: 'select',\n options: ['outlined', 'filled'],\n },\n size: {\n control: 'select',\n options: ['small', 'medium'],\n },\n label: {\n control: 'text',\n },\n placeholder: {\n control: 'text',\n },\n showFilters: {\n table: { disable: true },\n },\n },\n parameters: {\n design: {\n type: 'figma',\n url: 'https://www.figma.com/file/nmaoLeo69xBJCHm9nc6lEV/C4R-Components?type=design&node-id=1534-26505',\n },\n },\n} satisfies Meta\n\nconst top100Films = getTop100Films()\n\nconst GroupedNonVirtualizedTemplate = ({\n label,\n placeholder,\n error,\n size,\n required,\n helperText,\n disableCloseOnSelect,\n}: AutocompleteProps<FilmWithCategory, boolean> & AutocompleteInputProps) => {\n const [value, setValue] = useState<FilmWithCategory | null>(null)\n const [valueExtended, setValueExtended] =\n useState<FilmWithCategoryExtended | null>(null)\n\n // Smaller dataset suitable for non-virtualized rendering\n // Note: MUI's groupBy only groups consecutive items, it doesn't sort them.\n // We must sort by category to avoid repeated group headers.\n const filmsWithCategory: FilmWithCategory[] = top100Films\n .map((film) => ({\n ...film,\n category: (film.year ?? 0) < 1970 ? 'Classic' : 'Contemporary',\n }))\n .sort((a, b) => a.category.localeCompare(b.category))\n\n const filmsWithCategoryExtended: FilmWithCategoryExtended[] = top100Films\n .map((film) => ({\n ...film,\n secondaryText: `Released in ${film.year ?? 'Unknown'}`,\n extended: true,\n category: (film.year ?? 0) < 1970 ? 'Classic' : 'Contemporary',\n }))\n .sort((a, b) => a.category.localeCompare(b.category))\n\n const { singleRenderOption } = useAutocomplete({\n getOptionLabel: (option) => (option as FilmWithCategoryExtended).title,\n })\n\n return (\n <Grid container direction='column' spacing={6}>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Native groupBy (non-virtualized)'}</Label>\n <Autocomplete<FilmWithCategory, false>\n value={value}\n onChange={(_, newValue) => {\n setValue(newValue)\n }}\n options={filmsWithCategory}\n getOptionLabel={(option) => option.title}\n isOptionEqualToValue={(option, value) =>\n option.title === value.title\n }\n groupBy={(option) => option.category}\n renderGroup={(params) => (\n <React.Fragment key={params.key}>\n <MenuItem subtitle>{params.group}</MenuItem>\n {params.children}\n </React.Fragment>\n )}\n renderInput={(params) => (\n <TextField\n {...params}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n variant='outlined'\n error={error}\n size={size}\n required={required}\n InputLabelProps={{ shrink: true }}\n />\n )}\n size={size}\n disableCloseOnSelect={disableCloseOnSelect}\n />\n </Container>\n </Grid>\n <Grid item>\n <Container>\n <Label variant='body2'>\n {'Native groupBy (non-virtualized, extended)'}\n </Label>\n <Autocomplete<FilmWithCategoryExtended, false>\n value={valueExtended}\n onChange={(_, newValue) => {\n setValueExtended(newValue)\n }}\n options={filmsWithCategoryExtended}\n getOptionLabel={(option) => option.title}\n isOptionEqualToValue={(option, value) =>\n option.title === value.title\n }\n groupBy={(option) => option.category}\n renderGroup={(params) => (\n <React.Fragment key={params.key}>\n <MenuItem subtitle>{params.group}</MenuItem>\n {params.children}\n </React.Fragment>\n )}\n renderOption={singleRenderOption}\n renderInput={(params) => (\n <TextField\n {...params}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n variant='outlined'\n error={error}\n size={size}\n required={required}\n InputLabelProps={{ shrink: true }}\n />\n )}\n size={size}\n disableCloseOnSelect={disableCloseOnSelect}\n />\n </Container>\n </Grid>\n <Grid item>\n <DocContainer severity='info'>\n <Typography variant='body2' weight='strong' mb={2}>\n Non-Virtualized Grouped Autocomplete\n </Typography>\n <Typography variant='body2' mb={2}>\n This example uses MUI's native `groupBy` prop, which is ideal\n for smaller datasets where virtualization is not needed.\n </Typography>\n <Typography variant='body2' component='div' mb={2}>\n <DocHighlight component='span'>\n {`<Autocomplete\n options={options}\n groupBy={(option) => option.category}\n renderGroup={(params) => (\n <li key={params.key}>\n <MenuItem subtitle>{params.group}</MenuItem>\n <ul style={{ padding: 0 }}>{params.children}</ul>\n </li>\n )}\n/>`}\n </DocHighlight>\n </Typography>\n <Typography variant='body2' weight='strong' mb={1}>\n When to use:\n </Typography>\n <Typography variant='body2' component='div' mb={1}>\n • Small to medium datasets (under 100-200 items)\n </Typography>\n <Typography variant='body2' component='div' mb={1}>\n • When virtualization is not required\n </Typography>\n <Typography variant='body2' component='div' mb={1}>\n • Simple grouping needs without custom styling\n </Typography>\n <Typography variant='body2' mt={2}>\n <strong>Note:</strong> For large datasets, use the virtualized\n `GroupedVirtualization` story which uses\n `createAutocompleteGroupByList` to maintain performance.\n </Typography>\n </DocContainer>\n </Grid>\n </Grid>\n )\n}\n\nconst commonArgs = {\n label: 'Label text',\n placeholder: 'Placeholder text',\n helperText: 'Helper text.',\n}\n\nexport const GroupedNonVirtualized = {\n render: GroupedNonVirtualizedTemplate,\n args: { ...commonArgs, label: 'Grouped Dataset' },\n parameters: {\n docs: {\n description: {\n story:\n 'Non-virtualized grouped autocomplete using MUI native groupBy prop. Best suited for smaller datasets where virtualization is not needed. The native groupBy automatically renders group headers with proper styling.',\n },\n },\n },\n}"
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"name": "PseudoHover",
|
|
142
|
-
"id": "autocomplete--pseudo-hover",
|
|
143
|
-
"code": "const options = {\n title: 'Components/Autocomplete/Autocomplete',\n component: Autocomplete,\n tags: ['validated', 'custom-component'],\n args: {\n required: false,\n disabled: false,\n error: false,\n readOnly: false,\n loading: false,\n disableCloseOnSelect: false,\n },\n argTypes: {\n variant: {\n control: 'select',\n options: ['outlined', 'filled'],\n },\n size: {\n control: 'select',\n options: ['small', 'medium'],\n },\n label: {\n control: 'text',\n },\n placeholder: {\n control: 'text',\n },\n showFilters: {\n table: { disable: true },\n },\n },\n parameters: {\n design: {\n type: 'figma',\n url: 'https://www.figma.com/file/nmaoLeo69xBJCHm9nc6lEV/C4R-Components?type=design&node-id=1534-26505',\n },\n },\n} satisfies Meta\n\nconst top100Films = getTop100Films()\n\nconst VariantsTemplate = ({\n label,\n placeholder,\n helperText,\n error,\n size,\n required,\n disableCloseOnSelect,\n ...props\n}: AutocompleteProps<(typeof top100Films)[number]> &\n AutocompleteInputProps) => {\n return (\n <Grid container direction='column' spacing={6}>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Filled'}</Label>\n <Autocomplete\n {...props}\n options={top100Films}\n getOptionLabel={(option) => option.title}\n renderInput={(params) => (\n <TextField\n {...params}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n variant='filled'\n error={error}\n size={size}\n required={required}\n InputLabelProps={{ shrink: true }}\n />\n )}\n size={size}\n disableCloseOnSelect={disableCloseOnSelect}\n />\n </Container>\n </Grid>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Outlined'}</Label>\n <Autocomplete\n {...props}\n options={top100Films}\n getOptionLabel={(option) => option.title}\n renderInput={(params) => (\n <TextField\n {...params}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n variant='outlined'\n error={error}\n size={size}\n required={required}\n InputLabelProps={{ shrink: true }}\n />\n )}\n size={size}\n disableCloseOnSelect={disableCloseOnSelect}\n />\n </Container>\n </Grid>\n </Grid>\n )\n}\n\nconst commonArgs = {\n label: 'Label text',\n placeholder: 'Placeholder text',\n helperText: 'Helper text.',\n}\n\nconst disabledControlsVariantsArgTypes = {\n variant: { table: { disable: true } },\n}\n\nexport const PseudoHover = {\n render: VariantsTemplate,\n args: { ...commonArgs },\n argTypes: disabledControlsVariantsArgTypes,\n ...pseudoStory({ hover: true }),\n}"
|
|
144
129
|
}
|
|
145
130
|
]
|
|
146
131
|
},
|
|
@@ -216,11 +201,6 @@
|
|
|
216
201
|
"id": "button--playground",
|
|
217
202
|
"code": "const PlaygroundTemplate = (args: ButtonProps) => <Button {...args} />\n\nexport const Playground = {\n render: PlaygroundTemplate,\n args: { children: 'Button text' },\n parameters: {\n ...interactiveStoryParameters,\n },\n}"
|
|
218
203
|
},
|
|
219
|
-
{
|
|
220
|
-
"name": "Guide",
|
|
221
|
-
"id": "button--guide",
|
|
222
|
-
"code": "const DocTemplate = () => {\n return (\n <DocContainer severity='warning'>\n We have our own\n <DocLink href='https://github.com/CartoDB/meridian-ds/blob/main/src/components/Button/Button.tsx'>\n Button\n </DocLink>\n component that uses <i>Mui Button</i> and wraps its children in a{' '}\n <i>Typography</i> component to meet with the designed behavior (text\n overflow case).\n <Typography mt={2}>\n So, instead of Mui Button, you should use this one:\n <DocHighlight component='span'>components/Button</DocHighlight>\n </Typography>\n <Typography mt={2}>\n For external use:\n <DocHighlight component='span'>\n {'import { Button } from \"@carto/meridian-ds\"'}\n </DocHighlight>\n .\n </Typography>\n </DocContainer>\n )\n}\n\nexport const Guide = {\n render: DocTemplate,\n parameters: {\n ...documentationStoryParameters,\n },\n}"
|
|
223
|
-
},
|
|
224
204
|
{
|
|
225
205
|
"name": "Variants",
|
|
226
206
|
"id": "button--variants",
|
|
@@ -240,16 +220,6 @@
|
|
|
240
220
|
"name": "ExternalLink",
|
|
241
221
|
"id": "button--external-link",
|
|
242
222
|
"code": "const ExternalLinkTemplate = ({ children, ...rest }: ButtonProps) => {\n return (\n <Grid container direction='column' spacing={4}>\n <Grid item container direction='column'>\n <Typography variant='body1' sx={{ mb: 2 }}>\n {'External Link Buttons (with href)'}\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} variant='contained'>\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='outlined'>\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='text'>\n {children}\n </Button>\n </Grid>\n </Grid>\n </Grid>\n\n <Grid item container direction='column'>\n <Typography variant='body1' sx={{ mb: 2 }}>\n {'External with Custom End Icon (custom icon takes precedence)'}\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} endIcon={<Close />} variant='contained'>\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} endIcon={<Close />} variant='outlined'>\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} endIcon={<Close />} variant='text'>\n {children}\n </Button>\n </Grid>\n </Grid>\n </Grid>\n\n <Grid item container direction='column'>\n <Typography variant='body1' sx={{ mb: 2 }}>\n {'External without End Icon'}\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} showExternalIcon={false} variant='contained'>\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} showExternalIcon={false} variant='outlined'>\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} showExternalIcon={false} variant='text'>\n {children}\n </Button>\n </Grid>\n </Grid>\n </Grid>\n\n <Grid item container direction='column'>\n <Typography variant='body1' sx={{ mb: 2 }}>\n {\n 'External with Custom screenReaderText (custom text for screen readers)'\n }\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={4}>\n <Button\n {...rest}\n screenReaderText='Se abre en nueva pestaña'\n variant='contained'\n >\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button\n {...rest}\n screenReaderText='Se abre en nueva pestaña'\n variant='outlined'\n >\n {children}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button\n {...rest}\n screenReaderText='Se abre en nueva pestaña'\n variant='text'\n >\n {children}\n </Button>\n </Grid>\n </Grid>\n </Grid>\n </Grid>\n )\n}\n\nexport const ExternalLink = {\n render: ExternalLinkTemplate,\n args: {\n children: 'External Button',\n href: 'https://carto.com',\n external: true,\n },\n}"
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
"name": "PseudoHover",
|
|
246
|
-
"id": "button--pseudo-hover",
|
|
247
|
-
"code": "const ColorTemplate = ({ children, ...rest }: ButtonProps) => {\n const containedLabel = children ? children : 'Contained button'\n const outlinedLabel = children ? children : 'Outlined button'\n const textLabel = children ? children : 'Text button'\n\n return (\n <Grid container spacing={2}>\n <Grid item container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} variant='contained'>\n {containedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='contained' startIcon={<Add />}>\n {containedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='contained' endIcon={<Close />}>\n {containedLabel}\n </Button>\n </Grid>\n </Grid>\n <Grid item container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} variant='outlined'>\n {outlinedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='outlined' startIcon={<Add />}>\n {outlinedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='outlined' endIcon={<Close />}>\n {outlinedLabel}\n </Button>\n </Grid>\n </Grid>\n <Grid item container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} variant='text'>\n {textLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='text' startIcon={<Add />}>\n {textLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='text' endIcon={<Close />}>\n {textLabel}\n </Button>\n </Grid>\n </Grid>\n </Grid>\n )\n}\n\nconst AllColorsTemplate = (props: ButtonProps) => (\n <Grid container direction='column' spacing={4}>\n {colorsButton.map((color) => (\n <Grid item key={color}>\n <Typography variant='subtitle2' sx={{ mb: 1 }}>\n {color}\n </Typography>\n <ColorTemplate {...props} color={color as ButtonProps['color']} />\n </Grid>\n ))}\n </Grid>\n)\n\nexport const PseudoHover = {\n render: AllColorsTemplate,\n ...pseudoStory({ hover: true }),\n}"
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
"name": "PseudoFocusVisible",
|
|
251
|
-
"id": "button--pseudo-focus-visible",
|
|
252
|
-
"code": "const ColorTemplate = ({ children, ...rest }: ButtonProps) => {\n const containedLabel = children ? children : 'Contained button'\n const outlinedLabel = children ? children : 'Outlined button'\n const textLabel = children ? children : 'Text button'\n\n return (\n <Grid container spacing={2}>\n <Grid item container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} variant='contained'>\n {containedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='contained' startIcon={<Add />}>\n {containedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='contained' endIcon={<Close />}>\n {containedLabel}\n </Button>\n </Grid>\n </Grid>\n <Grid item container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} variant='outlined'>\n {outlinedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='outlined' startIcon={<Add />}>\n {outlinedLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='outlined' endIcon={<Close />}>\n {outlinedLabel}\n </Button>\n </Grid>\n </Grid>\n <Grid item container spacing={2}>\n <Grid item xs={4}>\n <Button {...rest} variant='text'>\n {textLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='text' startIcon={<Add />}>\n {textLabel}\n </Button>\n </Grid>\n <Grid item xs={4}>\n <Button {...rest} variant='text' endIcon={<Close />}>\n {textLabel}\n </Button>\n </Grid>\n </Grid>\n </Grid>\n )\n}\n\nconst AllColorsTemplate = (props: ButtonProps) => (\n <Grid container direction='column' spacing={4}>\n {colorsButton.map((color) => (\n <Grid item key={color}>\n <Typography variant='subtitle2' sx={{ mb: 1 }}>\n {color}\n </Typography>\n <ColorTemplate {...props} color={color as ButtonProps['color']} />\n </Grid>\n ))}\n </Grid>\n)\n\nexport const PseudoFocusVisible = {\n render: AllColorsTemplate,\n ...pseudoStory({ focusVisible: PSEUDO_SELECTORS.buttonBase }),\n}"
|
|
253
223
|
}
|
|
254
224
|
]
|
|
255
225
|
},
|
|
@@ -456,16 +426,6 @@
|
|
|
456
426
|
"name": "AccessibilityGuide",
|
|
457
427
|
"id": "icon-button--accessibility-guide",
|
|
458
428
|
"code": "const options = {\n title: 'Components/Buttons/IconButton',\n component: IconButton,\n tags: ['validated', 'custom-component'],\n argTypes: {\n color: {\n control: { type: 'select' },\n options: colorsButton,\n },\n size: {\n control: 'select',\n options: ['xsmall', 'small', 'medium', 'large'],\n },\n variant: {\n control: { type: 'select' },\n options: ['contained', 'outlined', 'icon'],\n },\n tooltip: {\n control: 'text',\n },\n tooltipPlacement: {\n table: {\n defaultValue: { summary: 'top' },\n },\n control: 'select',\n options: ['top', 'left', 'right', 'bottom'],\n },\n },\n args: {\n disabled: false,\n loading: false,\n active: false,\n },\n parameters: {\n design: {\n type: 'figma',\n url: 'https://www.figma.com/file/nmaoLeo69xBJCHm9nc6lEV/CARTO-Components-1.0?node-id=1761%3A30090',\n },\n },\n} satisfies Meta\n\nconst AccessibilityGuideTemplate = () => {\n const code = `// ✅ Good - tests using accessible name\nexpect(screen.getByRole('button', { name: 'Updated' })).toBeVisible()\n\n// ❌ Avoid - relies on implementation details\nexpect(screen.getByTestId('CheckCircleOutlinedIcon')).toBeVisible()`\n\n return (\n <>\n <Typography variant='h6' sx={{ mb: 2 }}>\n Accessibility Guidelines\n </Typography>\n <Typography variant='body2' sx={{ mb: 3 }}>\n IconButtons must have accessible labels for screen readers. Use the{' '}\n <i>aria-label</i> prop to provide descriptive text.\n </Typography>\n\n <Typography variant='h6' sx={{ mb: 2, mt: 4 }}>\n Good Example\n </Typography>\n <Box>\n <IconButton\n icon={<CheckCircleOutlined />}\n aria-label='Updated'\n variant='outlined'\n />\n <Typography variant='caption' display='block' sx={{ mt: 1 }}>\n aria-label="Updated"\n </Typography>\n </Box>\n\n <Typography variant='h6' sx={{ mb: 2, mt: 4 }}>\n Unit Testing\n </Typography>\n <CodeAreaField\n label='Test IconButtons using their accessible name'\n options={{ readOnly: true, mode: 'javascript' }}\n copyValue={code}\n value={code}\n />\n </>\n )\n}\n\nexport const AccessibilityGuide = {\n render: AccessibilityGuideTemplate,\n parameters: {\n controls: { disable: true },\n docs: {\n description: {\n story: 'Guidelines for making IconButtons accessible and testable.',\n },\n canvas: { sourceState: 'hidden' },\n },\n chromatic: { disableSnapshot: true },\n },\n}"
|
|
459
|
-
},
|
|
460
|
-
{
|
|
461
|
-
"name": "PseudoHover",
|
|
462
|
-
"id": "icon-button--pseudo-hover",
|
|
463
|
-
"code": "const SizeTemplate = (rest: IconButtonProps) => {\n return (\n <Grid container spacing={2}>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Icon\n </Typography>\n <IconButton {...rest} variant='icon' icon={<CheckCircleOutlined />} />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Contained\n </Typography>\n <IconButton\n {...rest}\n variant='contained'\n icon={<CheckCircleOutlined />}\n />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Outlined\n </Typography>\n <IconButton\n {...rest}\n variant='outlined'\n icon={<CheckCircleOutlined />}\n />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Disabled\n </Typography>\n <IconButton {...rest} disabled icon={<CheckCircleOutlined />} />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Loading\n </Typography>\n <IconButton {...rest} loading icon={<CheckCircleOutlined />} />\n </Grid>\n </Grid>\n </Grid>\n )\n}\n\nconst colors = ['default', 'primary', 'secondary', 'error', 'warning'] as const\n\nconst ColorsTemplate = (rest: IconButtonProps) => (\n <>\n {colors.map((color) => (\n <Box key={color} mb={6}>\n <Typography variant='h6' sx={{ mb: 4, textTransform: 'capitalize' }}>\n {color}\n </Typography>\n <SizeTemplate {...rest} color={color} />\n </Box>\n ))}\n </>\n)\n\nexport const PseudoHover = {\n render: ColorsTemplate,\n ...pseudoStory({ hover: true }),\n}"
|
|
464
|
-
},
|
|
465
|
-
{
|
|
466
|
-
"name": "PseudoFocusVisible",
|
|
467
|
-
"id": "icon-button--pseudo-focus-visible",
|
|
468
|
-
"code": "const SizeTemplate = (rest: IconButtonProps) => {\n return (\n <Grid container spacing={2}>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Icon\n </Typography>\n <IconButton {...rest} variant='icon' icon={<CheckCircleOutlined />} />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Contained\n </Typography>\n <IconButton\n {...rest}\n variant='contained'\n icon={<CheckCircleOutlined />}\n />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Outlined\n </Typography>\n <IconButton\n {...rest}\n variant='outlined'\n icon={<CheckCircleOutlined />}\n />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Disabled\n </Typography>\n <IconButton {...rest} disabled icon={<CheckCircleOutlined />} />\n </Grid>\n </Grid>\n <Grid item xs={2}>\n <Grid container direction='column' alignItems='center' spacing={2}>\n <Typography variant='body2' sx={{ mb: 2 }}>\n Loading\n </Typography>\n <IconButton {...rest} loading icon={<CheckCircleOutlined />} />\n </Grid>\n </Grid>\n </Grid>\n )\n}\n\nconst colors = ['default', 'primary', 'secondary', 'error', 'warning'] as const\n\nconst ColorsTemplate = (rest: IconButtonProps) => (\n <>\n {colors.map((color) => (\n <Box key={color} mb={6}>\n <Typography variant='h6' sx={{ mb: 4, textTransform: 'capitalize' }}>\n {color}\n </Typography>\n <SizeTemplate {...rest} color={color} />\n </Box>\n ))}\n </>\n)\n\nexport const PseudoFocusVisible = {\n render: ColorsTemplate,\n ...pseudoStory({ focusVisible: PSEUDO_SELECTORS.buttonBase }),\n}"
|
|
469
429
|
}
|
|
470
430
|
]
|
|
471
431
|
},
|
|
@@ -548,11 +508,6 @@
|
|
|
548
508
|
"id": "select-field--interaction",
|
|
549
509
|
"code": "const menuItems = [\n { label: 'Ten: super large text with overflow', id: '10' },\n { label: 'Twenty', id: '20' },\n { label: 'Thirty', id: '30' },\n]\n\nconst SelectFieldItem = ({\n label,\n required,\n placeholder,\n variant,\n helperText,\n size,\n focused,\n disabled,\n error,\n ...rest\n}: SelectFieldProps) => {\n const [content, setContent] = useState([] as string[])\n\n const handleChange = (event: SelectChangeEvent<unknown>) => {\n const value = event.target.value\n setContent(\n // On autofill we get a stringified value\n typeof value === 'string' ? value.split(',') : (value as string[]),\n )\n }\n\n return (\n <SelectField\n {...rest}\n variant={variant}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n value={content}\n focused={focused}\n disabled={disabled}\n onChange={handleChange}\n error={error}\n size={size}\n fullWidth={rest.fullWidth}\n required={required}\n >\n {menuItems.map((o, i) => (\n <MenuItem key={i} value={o.label}>\n {o.label}\n </MenuItem>\n ))}\n </SelectField>\n )\n}\n\nconst PlaygroundTemplate = (args: SelectFieldProps) => (\n <SelectFieldItem {...args} />\n)\n\nconst commonArgs = {\n label: 'Label text',\n placeholder: 'Placeholder text',\n helperText: 'Helper text.',\n}\n\nconst disabledControlsArgTypes = {\n variant: { table: { disable: true } },\n multiple: { table: { disable: true } },\n}\n\nexport const Interaction = {\n render: PlaygroundTemplate,\n tags: ['interaction', '!autodocs'],\n args: { ...commonArgs },\n argTypes: disabledControlsArgTypes,\n play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {\n const canvas = within(canvasElement)\n const combobox = canvas.getByRole('combobox')\n\n await userEvent.click(combobox)\n const listbox = await screen.findByRole('listbox')\n await waitFor(() => expect(listbox).toBeVisible())\n\n await userEvent.click(\n within(listbox).getByRole('option', { name: 'Twenty' }),\n )\n await waitFor(() =>\n expect(screen.queryByRole('listbox')).not.toBeInTheDocument(),\n )\n await expect(combobox).toHaveTextContent('Twenty')\n\n // Reopen and pick another option — single-select replaces the previous\n // value rather than accumulating it.\n await userEvent.click(combobox)\n const reopenedListbox = await screen.findByRole('listbox')\n await userEvent.click(\n within(reopenedListbox).getByRole('option', { name: 'Thirty' }),\n )\n await waitFor(() =>\n expect(screen.queryByRole('listbox')).not.toBeInTheDocument(),\n )\n await expect(combobox).toHaveTextContent('Thirty')\n await expect(combobox).not.toHaveTextContent('Twenty')\n },\n}"
|
|
550
510
|
},
|
|
551
|
-
{
|
|
552
|
-
"name": "Guide",
|
|
553
|
-
"id": "select-field--guide",
|
|
554
|
-
"code": "const DocTemplate = () => {\n return (\n <DocContainer severity='warning'>\n This component adds the <i>placeholder</i> logic on top of TextField Mui\n component.\n <Typography mt={2}>\n So, instead of <i>{'<TextField select />'}</i> or <i>{'<Select />'}</i>,\n you should use this one:\n <DocHighlight component='span'>components/SelectField</DocHighlight>\n </Typography>\n <Typography mt={2}>\n For external use:\n <DocHighlight component='span'>\n {'import { SelectField } from \"@carto/meridian-ds\"'}\n </DocHighlight>\n .\n </Typography>\n </DocContainer>\n )\n}\n\nexport const Guide = {\n render: DocTemplate,\n parameters: {\n ...documentationStoryParameters,\n },\n}"
|
|
555
|
-
},
|
|
556
511
|
{
|
|
557
512
|
"name": "Variants",
|
|
558
513
|
"id": "select-field--variants",
|
|
@@ -587,11 +542,6 @@
|
|
|
587
542
|
"name": "Grouped",
|
|
588
543
|
"id": "select-field--grouped",
|
|
589
544
|
"code": "const groupedMenuItems: GroupedItem[] = [\n {\n label: 'The Shawshank Redemption',\n id: '1',\n category: 'Drama',\n secondaryText: '1994 · Frank Darabont',\n },\n {\n label: 'The Godfather',\n id: '2',\n category: 'Drama',\n secondaryText: '1972 · Francis Ford Coppola',\n },\n {\n label: \"Schindler's List\",\n id: '3',\n category: 'Drama',\n secondaryText: '1993 · Steven Spielberg',\n },\n {\n label: 'The Dark Knight',\n id: '4',\n category: 'Action',\n secondaryText: '2008 · Christopher Nolan',\n },\n {\n label: 'Inception',\n id: '5',\n category: 'Action',\n secondaryText: '2010 · Christopher Nolan',\n },\n {\n label: 'The Matrix',\n id: '6',\n category: 'Action',\n secondaryText: '1999 · The Wachowskis',\n },\n {\n label: 'Pulp Fiction',\n id: '7',\n category: 'Crime',\n secondaryText: '1994 · Quentin Tarantino',\n },\n {\n label: 'The Usual Suspects',\n id: '8',\n category: 'Crime',\n secondaryText: '1995 · Bryan Singer',\n },\n]\n\nconst GroupedTemplate = ({\n label,\n placeholder,\n helperText,\n size = 'small',\n ...rest\n}: SelectFieldProps) => {\n const [value, setValue] = useState('')\n const [valueExtended, setValueExtended] = useState('')\n\n const handleChange = (event: SelectChangeEvent<unknown>) => {\n setValue(event.target.value as string)\n }\n\n const handleChangeExtended = (event: SelectChangeEvent<unknown>) => {\n setValueExtended(event.target.value as string)\n }\n\n // Group items by category\n const groupedByCategory = groupedMenuItems.reduce<\n Record<string, GroupedItem[]>\n >((acc, item) => {\n const category = item.category\n const categoryItems = acc[category] ?? []\n return {\n ...acc,\n [category]: [...categoryItems, item],\n }\n }, {})\n\n // Render grouped items with MenuItem subtitle for group titles\n const renderGroupedItems = (extended: boolean) => {\n const items: React.ReactNode[] = []\n Object.entries(groupedByCategory).forEach(([category, categoryItems]) => {\n items.push(\n <MenuItem\n key={category}\n subtitle\n component='div'\n tabIndex={-1}\n disabled\n >\n {category}\n </MenuItem>,\n )\n categoryItems.forEach((item) => {\n items.push(\n <MenuItem key={item.id} value={item.id} extended={extended}>\n {extended ? (\n <ListItemText\n primary={item.label}\n secondary={item.secondaryText}\n />\n ) : (\n item.label\n )}\n </MenuItem>,\n )\n })\n })\n return items\n }\n\n return (\n <Grid container direction='column' spacing={6}>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Grouped (default)'}</Label>\n <SelectField\n {...rest}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n value={value}\n onChange={handleChange}\n size={size}\n >\n {renderGroupedItems(false)}\n </SelectField>\n </Container>\n </Grid>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Grouped (extended)'}</Label>\n <SelectField\n {...rest}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n value={valueExtended}\n onChange={handleChangeExtended}\n size={size}\n displayEmpty\n renderValue={(selected) => {\n if (!selected) {\n return (\n <Typography\n component='span'\n variant={size === 'small' ? 'body2' : 'body1'}\n color='text.hint'\n >\n {placeholder}\n </Typography>\n )\n }\n const item = groupedMenuItems.find((i) => i.id === selected)\n return item?.label\n }}\n >\n {renderGroupedItems(true)}\n </SelectField>\n </Container>\n </Grid>\n <Grid item>\n <DocContainer severity='info'>\n <Typography variant='body2' weight='strong' mb={2}>\n Grouped SelectField\n </Typography>\n <Typography variant='body2' mb={2}>\n Use `MenuItem` with `subtitle` prop to create group headers in\n SelectField. Group headers are non-selectable and visually separate\n option categories.\n </Typography>\n <Typography variant='body2' component='div' mb={2}>\n <DocHighlight component='span'>\n {`<SelectField>\n <MenuItem subtitle component=\"div\" tabIndex={-1} disabled>\n Category Name\n </MenuItem>\n <MenuItem value=\"1\">Option 1</MenuItem>\n <MenuItem value=\"2\">Option 2</MenuItem>\n</SelectField>`}\n </DocHighlight>\n </Typography>\n <Typography variant='body2' weight='strong' mb={1}>\n Key points:\n </Typography>\n <Typography variant='body2' component='div' mb={1}>\n • Use `MenuItem subtitle` with `tabIndex={-1}` and `disabled` for\n group headers\n </Typography>\n <Typography variant='body2' component='div' mb={1}>\n • Add `component="div"` to prevent nesting `li` elements\n </Typography>\n <Typography variant='body2' component='div' mb={1}>\n • For extended height items, add `extended` prop to all MenuItem\n components\n </Typography>\n </DocContainer>\n </Grid>\n </Grid>\n )\n}\n\nconst commonArgs = {\n label: 'Label text',\n placeholder: 'Placeholder text',\n helperText: 'Helper text.',\n}\n\nconst disabledControlsArgTypes = {\n variant: { table: { disable: true } },\n multiple: { table: { disable: true } },\n}\n\nexport const Grouped = {\n render: GroupedTemplate,\n args: { ...commonArgs },\n argTypes: disabledControlsArgTypes,\n parameters: {\n docs: {\n description: {\n story:\n 'Grouped SelectField example using ListSubheader for category headers. Shows both default and extended height variants.',\n },\n },\n },\n}"
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
"name": "PseudoHover",
|
|
593
|
-
"id": "select-field--pseudo-hover",
|
|
594
|
-
"code": "const menuItems = [\n { label: 'Ten: super large text with overflow', id: '10' },\n { label: 'Twenty', id: '20' },\n { label: 'Thirty', id: '30' },\n]\n\nconst SelectFieldItem = ({\n label,\n required,\n placeholder,\n variant,\n helperText,\n size,\n focused,\n disabled,\n error,\n ...rest\n}: SelectFieldProps) => {\n const [content, setContent] = useState([] as string[])\n\n const handleChange = (event: SelectChangeEvent<unknown>) => {\n const value = event.target.value\n setContent(\n // On autofill we get a stringified value\n typeof value === 'string' ? value.split(',') : (value as string[]),\n )\n }\n\n return (\n <SelectField\n {...rest}\n variant={variant}\n label={label}\n placeholder={placeholder}\n helperText={helperText}\n value={content}\n focused={focused}\n disabled={disabled}\n onChange={handleChange}\n error={error}\n size={size}\n fullWidth={rest.fullWidth}\n required={required}\n >\n {menuItems.map((o, i) => (\n <MenuItem key={i} value={o.label}>\n {o.label}\n </MenuItem>\n ))}\n </SelectField>\n )\n}\n\nconst VariantsTemplate = ({\n label,\n placeholder,\n ...rest\n}: SelectFieldProps) => {\n return (\n <Grid container direction='column' spacing={6}>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Filled'}</Label>\n <SelectFieldItem\n {...rest}\n label={label}\n variant='filled'\n placeholder={placeholder}\n />\n </Container>\n </Grid>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Outlined'}</Label>\n <SelectFieldItem\n {...rest}\n label={label}\n variant='outlined'\n placeholder={placeholder}\n />\n </Container>\n </Grid>\n <Grid item>\n <Container>\n <Label variant='body2'>{'Standard'}</Label>\n <SelectFieldItem\n {...rest}\n label={label}\n variant='standard'\n placeholder={placeholder}\n />\n </Container>\n </Grid>\n </Grid>\n )\n}\n\nconst commonArgs = {\n label: 'Label text',\n placeholder: 'Placeholder text',\n helperText: 'Helper text.',\n}\n\nconst disabledControlsArgTypes = {\n variant: { table: { disable: true } },\n multiple: { table: { disable: true } },\n}\n\nexport const PseudoHover = {\n render: VariantsTemplate,\n args: { ...commonArgs },\n argTypes: disabledControlsArgTypes,\n ...pseudoStory({ hover: true }),\n}"
|
|
595
545
|
}
|
|
596
546
|
]
|
|
597
547
|
},
|
|
@@ -661,11 +611,6 @@
|
|
|
661
611
|
"id": "split-button--interaction-disabled-tooltip",
|
|
662
612
|
"code": "const DEFAULT_OPTIONS = [\n { label: 'Item 1', disabled: false },\n { label: 'Item 2', disabled: false },\n { label: 'Item 3', disabled: true },\n]\n\nconst options = {\n title: 'Components/Buttons/SplitButton',\n component: SplitButton,\n tags: ['validated', 'custom-component'],\n argTypes: {\n variant: {\n table: {\n defaultValue: { summary: \"'contained'\" },\n },\n control: 'select',\n options: ['text', 'contained', 'outlined'],\n },\n color: {\n table: {\n defaultValue: { summary: \"'primary'\" },\n },\n control: 'select',\n options: ['default', 'primary', 'secondary', 'error'],\n },\n size: {\n control: 'select',\n options: ['small', 'medium', 'large'],\n },\n loading: {\n table: {\n defaultValue: { summary: 'false' },\n },\n control: 'boolean',\n },\n disabled: {\n table: {\n defaultValue: { summary: 'false' },\n },\n control: 'boolean',\n },\n label: {\n control: 'text',\n },\n },\n args: {\n variant: 'contained',\n color: 'primary',\n label: 'Split Button Text',\n disabled: false,\n },\n parameters: {\n design: {\n type: 'figma',\n url: 'https://www.figma.com/design/nmaoLeo69xBJCHm9nc6lEV/Meridian-DS?node-id=22133-3949&p=f&t=gZJ8CCQBU12LHMmM-0',\n },\n },\n} satisfies Meta\n\nconst PlaygroundTemplate = ({ size, variant, ...rest }: SplitButtonProps) => (\n <div style={{ minHeight: 200 }}>\n <SplitButton\n {...rest}\n size={size}\n variant={variant}\n options={DEFAULT_OPTIONS}\n onClick={(option) => {\n console.log('Clicked option:', option)\n }}\n />\n </div>\n)\n\nexport const InteractionDisabledTooltip = {\n render: PlaygroundTemplate,\n tags: ['interaction', '!autodocs'],\n args: { disabled: true },\n parameters: {\n ...chromaticTransitionDelay,\n docs: {\n description: {\n story:\n 'A disabled SplitButton still surfaces its toggle tooltip — the toggle Button is wrapped in a span so MUI Tooltip can listen to events even when the underlying button is disabled.',\n },\n },\n },\n play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {\n const canvas = within(canvasElement)\n const toggle = canvas.getByRole('button', { name: /show options/i })\n await expect(toggle).toBeDisabled()\n await expectTooltipOnHover(toggle, /^Show options$/)\n },\n}"
|
|
663
613
|
},
|
|
664
|
-
{
|
|
665
|
-
"name": "Guide",
|
|
666
|
-
"id": "split-button--guide",
|
|
667
|
-
"code": "const DocTemplate = () => {\n return (\n <DocContainer severity='warning'>\n We have our own\n <DocLink href='https://github.com/CartoDB/meridian-ds/blob/main/src/components/SplitButton/SplitButton.tsx'>\n SplitButton\n </DocLink>\n component that uses <i>MUI ButtonGroup</i>\n <Typography mt={2}>\n So, instead of MUI ButtonGroup, you should use this one:\n <DocHighlight component='span'>components/SplitButton</DocHighlight>\n </Typography>\n <Typography mt={2}>\n For external use:\n <DocHighlight component='span'>\n {'import { SplitButton } from \"@carto/meridian-ds\"'}\n </DocHighlight>\n .\n </Typography>\n </DocContainer>\n )\n}\n\nexport const Guide = {\n render: DocTemplate,\n parameters: {\n ...documentationStoryParameters,\n },\n}"
|
|
668
|
-
},
|
|
669
614
|
{
|
|
670
615
|
"name": "Variants",
|
|
671
616
|
"id": "split-button--variants",
|
|
@@ -680,11 +625,6 @@
|
|
|
680
625
|
"name": "Colors",
|
|
681
626
|
"id": "split-button--colors",
|
|
682
627
|
"code": "const SPLIT_BUTTON_COLORS = [\n 'default',\n 'primary',\n 'secondary',\n 'error',\n] as const\n\nconst DEFAULT_OPTIONS = [\n { label: 'Item 1', disabled: false },\n { label: 'Item 2', disabled: false },\n { label: 'Item 3', disabled: true },\n]\n\nconst options = {\n title: 'Components/Buttons/SplitButton',\n component: SplitButton,\n tags: ['validated', 'custom-component'],\n argTypes: {\n variant: {\n table: {\n defaultValue: { summary: \"'contained'\" },\n },\n control: 'select',\n options: ['text', 'contained', 'outlined'],\n },\n color: {\n table: {\n defaultValue: { summary: \"'primary'\" },\n },\n control: 'select',\n options: ['default', 'primary', 'secondary', 'error'],\n },\n size: {\n control: 'select',\n options: ['small', 'medium', 'large'],\n },\n loading: {\n table: {\n defaultValue: { summary: 'false' },\n },\n control: 'boolean',\n },\n disabled: {\n table: {\n defaultValue: { summary: 'false' },\n },\n control: 'boolean',\n },\n label: {\n control: 'text',\n },\n },\n args: {\n variant: 'contained',\n color: 'primary',\n label: 'Split Button Text',\n disabled: false,\n },\n parameters: {\n design: {\n type: 'figma',\n url: 'https://www.figma.com/design/nmaoLeo69xBJCHm9nc6lEV/Meridian-DS?node-id=22133-3949&p=f&t=gZJ8CCQBU12LHMmM-0',\n },\n },\n} satisfies Meta\n\nconst ColorTemplate = ({ color, ...rest }: SplitButtonProps) => {\n return (\n <div style={{ minHeight: 200 }}>\n <SplitButton\n {...rest}\n color={color}\n defaultOpen\n options={DEFAULT_OPTIONS}\n />\n </div>\n )\n}\n\nconst AllColorsTemplate = (props: SplitButtonProps) => (\n <Grid container direction='row' spacing={4}>\n {SPLIT_BUTTON_COLORS.map((color) => (\n <Grid item key={color}>\n <Typography\n variant='subtitle2'\n sx={{ mb: 1, textTransform: 'capitalize' }}\n >\n {color}\n </Typography>\n <ColorTemplate {...props} color={color} />\n </Grid>\n ))}\n </Grid>\n)\n\nexport const Colors = {\n render: AllColorsTemplate,\n}"
|
|
683
|
-
},
|
|
684
|
-
{
|
|
685
|
-
"name": "PseudoHover",
|
|
686
|
-
"id": "split-button--pseudo-hover",
|
|
687
|
-
"code": "const SPLIT_BUTTON_VARIANTS = ['contained', 'outlined', 'text'] as const\n\nconst SPLIT_BUTTON_COLORS = [\n 'default',\n 'primary',\n 'secondary',\n 'error',\n] as const\n\nconst DEFAULT_OPTIONS = [\n { label: 'Item 1', disabled: false },\n { label: 'Item 2', disabled: false },\n { label: 'Item 3', disabled: true },\n]\n\nconst options = {\n title: 'Components/Buttons/SplitButton',\n component: SplitButton,\n tags: ['validated', 'custom-component'],\n argTypes: {\n variant: {\n table: {\n defaultValue: { summary: \"'contained'\" },\n },\n control: 'select',\n options: ['text', 'contained', 'outlined'],\n },\n color: {\n table: {\n defaultValue: { summary: \"'primary'\" },\n },\n control: 'select',\n options: ['default', 'primary', 'secondary', 'error'],\n },\n size: {\n control: 'select',\n options: ['small', 'medium', 'large'],\n },\n loading: {\n table: {\n defaultValue: { summary: 'false' },\n },\n control: 'boolean',\n },\n disabled: {\n table: {\n defaultValue: { summary: 'false' },\n },\n control: 'boolean',\n },\n label: {\n control: 'text',\n },\n },\n args: {\n variant: 'contained',\n color: 'primary',\n label: 'Split Button Text',\n disabled: false,\n },\n parameters: {\n design: {\n type: 'figma',\n url: 'https://www.figma.com/design/nmaoLeo69xBJCHm9nc6lEV/Meridian-DS?node-id=22133-3949&p=f&t=gZJ8CCQBU12LHMmM-0',\n },\n },\n} satisfies Meta\n\nconst AllVariantsAndColorsTemplate = (props: SplitButtonProps) => (\n <Grid container direction='column' spacing={4}>\n {SPLIT_BUTTON_VARIANTS.map((variant) => (\n <Grid item key={variant}>\n <Typography\n variant='subtitle2'\n sx={{ mb: 1, textTransform: 'capitalize' }}\n >\n {variant}\n </Typography>\n <Grid container spacing={2}>\n {SPLIT_BUTTON_COLORS.map((color) => (\n <Grid item key={color}>\n <div style={{ minHeight: 160 }}>\n <SplitButton\n {...props}\n variant={variant}\n color={color}\n defaultOpen\n options={DEFAULT_OPTIONS}\n />\n </div>\n </Grid>\n ))}\n </Grid>\n </Grid>\n ))}\n </Grid>\n)\n\nexport const PseudoHover = {\n render: AllVariantsAndColorsTemplate,\n ...pseudoStory({ hover: true }),\n}"
|
|
688
628
|
}
|
|
689
629
|
]
|
|
690
630
|
},
|
|
@@ -52,11 +52,15 @@ var TOOLS = [
|
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
name: "meridian_get_examples",
|
|
55
|
-
description: "Real, copy-able example code for a component (one entry per Storybook story: name, id, source), shipped in the package.",
|
|
55
|
+
description: "Real, copy-able example code for a component (one entry per Storybook story: name, id, source), shipped in the package. With no storyId, returns every story's code \u2014 or, when that would be too large for one response, an index of {name, id} to fetch individually. Pass a storyId (from the index or meridian_get_component) to get a single story's full code.",
|
|
56
56
|
inputSchema: {
|
|
57
57
|
type: "object",
|
|
58
58
|
properties: {
|
|
59
|
-
name: { type: "string", description: 'Component name, e.g. "Button"' }
|
|
59
|
+
name: { type: "string", description: 'Component name, e.g. "Button"' },
|
|
60
|
+
storyId: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "Optional. A story id (or name) to fetch just that one example \u2014 use for components whose full set is too large to return at once."
|
|
63
|
+
}
|
|
60
64
|
},
|
|
61
65
|
required: ["name"],
|
|
62
66
|
additionalProperties: false
|
|
@@ -96,15 +100,36 @@ function getComponent(name) {
|
|
|
96
100
|
promotedMuiProps: doc.curation.mui ?? []
|
|
97
101
|
},
|
|
98
102
|
examples: {
|
|
99
|
-
note: "Call meridian_get_examples for the source
|
|
103
|
+
note: "Call meridian_get_examples for the source of these stories (pass a storyId for one at a time if the full set is large).",
|
|
100
104
|
storyIds: doc.examples.map((e) => e.id)
|
|
101
105
|
}
|
|
102
106
|
};
|
|
103
107
|
}
|
|
104
|
-
|
|
108
|
+
var EXAMPLES_INLINE_LIMIT = 3e4;
|
|
109
|
+
function getComponentExamples(name, storyId) {
|
|
105
110
|
const doc = find(name);
|
|
106
111
|
if (!doc) return notFound(name);
|
|
107
|
-
|
|
112
|
+
if (storyId) {
|
|
113
|
+
const ex = doc.examples.find((e) => e.id === storyId || e.name === storyId);
|
|
114
|
+
if (!ex)
|
|
115
|
+
return {
|
|
116
|
+
error: `Unknown story "${storyId}" for ${doc.name}. Available: ${doc.examples.map((e) => e.id).join(", ")}`
|
|
117
|
+
};
|
|
118
|
+
return { component: doc.name, example: ex };
|
|
119
|
+
}
|
|
120
|
+
const total = doc.examples.reduce((n, e) => n + e.code.length, 0);
|
|
121
|
+
if (total <= EXAMPLES_INLINE_LIMIT) {
|
|
122
|
+
return { component: doc.name, examples: doc.examples };
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
component: doc.name,
|
|
126
|
+
note: `${doc.examples.length} examples (~${Math.round(total / 1024)} KB) \u2014 too large to return at once. Call meridian_get_examples again with a storyId from the list below for one story's full code.`,
|
|
127
|
+
examples: doc.examples.map((e) => ({
|
|
128
|
+
name: e.name,
|
|
129
|
+
id: e.id,
|
|
130
|
+
bytes: e.code.length
|
|
131
|
+
}))
|
|
132
|
+
};
|
|
108
133
|
}
|
|
109
134
|
function callTool(name, args) {
|
|
110
135
|
switch (name) {
|
|
@@ -113,7 +138,10 @@ function callTool(name, args) {
|
|
|
113
138
|
case "meridian_get_component":
|
|
114
139
|
return getComponent(String(args.name ?? ""));
|
|
115
140
|
case "meridian_get_examples":
|
|
116
|
-
return getComponentExamples(
|
|
141
|
+
return getComponentExamples(
|
|
142
|
+
String(args.name ?? ""),
|
|
143
|
+
args.storyId ? String(args.storyId) : void 0
|
|
144
|
+
);
|
|
117
145
|
case "meridian_get_conventions":
|
|
118
146
|
return { conventions: CONVENTIONS };
|
|
119
147
|
default:
|