@orangesk/orange-design-system 2.0.0-beta.47 → 2.0.0-beta.49
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.
- package/build/components/BlockAction/style.css +1 -1
- package/build/components/BlockAction/style.css.map +1 -1
- package/build/components/Breadcrumbs/style.css +1 -1
- package/build/components/Breadcrumbs/style.css.map +1 -1
- package/build/components/Card/style.css +1 -1
- package/build/components/Card/style.css.map +1 -1
- package/build/components/DocumentationSidebar/style.css +1 -1
- package/build/components/DocumentationSidebar/style.css.map +1 -1
- package/build/components/Icon/style.css +1 -1
- package/build/components/Icon/style.css.map +1 -1
- package/build/components/Loader/style.css +1 -1
- package/build/components/Loader/style.css.map +1 -1
- package/build/components/Megamenu/style.css +1 -1
- package/build/components/Megamenu/style.css.map +1 -1
- package/build/components/Pagination/style.css +1 -1
- package/build/components/Pagination/style.css.map +1 -1
- package/build/components/PromotionCard/style.css +1 -1
- package/build/components/PromotionCard/style.css.map +1 -1
- package/build/components/Section/style.css +1 -1
- package/build/components/Section/style.css.map +1 -1
- package/build/components/SocialButton/style.css +1 -1
- package/build/components/SocialButton/style.css.map +1 -1
- package/build/components/Stepbar/style.css +1 -1
- package/build/components/Stepbar/style.css.map +1 -1
- package/build/components/Table/style.css +1 -1
- package/build/components/Table/style.css.map +1 -1
- package/build/components/Tag/style.css +1 -1
- package/build/components/Tag/style.css.map +1 -1
- package/build/components/Tile/style.css +1 -1
- package/build/components/Tile/style.css.map +1 -1
- package/build/components/index.js +1 -1
- package/build/components/index.js.map +1 -1
- package/build/components/tsconfig.tsbuildinfo +1 -1
- package/build/components/types/index.d.ts +5 -11
- package/build/components/types/src/components/Carousel/Carousel.static.d.ts +4 -1
- package/build/components/types/src/components/CarouselHero/CarouselHero.d.ts +1 -0
- package/build/components/types/src/components/Forms/Autocomplete/Autocomplete.static.d.ts +11 -0
- package/build/components/types/src/components/Forms/Group/Group.d.ts +1 -1
- package/build/components/types/src/components/Tile/Tile.d.ts +3 -11
- package/build/lib/base.css +1 -1
- package/build/lib/base.css.map +1 -1
- package/build/lib/components.css +1 -1
- package/build/lib/components.css.map +1 -1
- package/build/lib/megamenu.css +1 -1
- package/build/lib/megamenu.css.map +1 -1
- package/build/lib/scripts.js +1 -1
- package/build/lib/scripts.js.map +1 -1
- package/build/lib/style.css +1 -1
- package/build/lib/style.css.map +1 -1
- package/build/lib/tsconfig.tsbuildinfo +1 -1
- package/build/lib/utilities.css +1 -1
- package/build/lib/utilities.css.map +1 -1
- package/build/search-index.json +5 -5
- package/package.json +21 -21
- package/src/components/BlockAction/styles/style.scss +6 -4
- package/src/components/Breadcrumbs/styles/style.scss +2 -1
- package/src/components/Card/styles/config.scss +1 -1
- package/src/components/Carousel/Carousel.static.ts +29 -1
- package/src/components/Carousel/tests/Carousel.static.test.jsx +50 -0
- package/src/components/CarouselHero/CarouselHero.static.ts +7 -0
- package/src/components/CarouselHero/CarouselHero.tsx +61 -3
- package/src/components/CarouselHero/tests/CarouselHero.unit.test.jsx +31 -1
- package/src/components/DocumentationSidebar/DocumentationSidebar.tsx +21 -34
- package/src/components/DocumentationSidebar/styles/style.scss +0 -6
- package/src/components/Forms/Autocomplete/Autocomplete.static.ts +190 -14
- package/src/components/Forms/Autocomplete/styles/style.scss +61 -8
- package/src/components/Forms/Autocomplete/tests/Autocomplete.static.test.ts +187 -0
- package/src/components/Forms/DatePicker/styles/style.scss +1 -2
- package/src/components/Forms/Group/Group.tsx +4 -1
- package/src/components/Forms/Group/styles/config.scss +1 -1
- package/src/components/Forms/Group/styles/mixins.scss +17 -0
- package/src/components/Forms/Group/tests/Group.unit.test.jsx +9 -0
- package/src/components/Forms/TextArea/styles/config.scss +1 -0
- package/src/components/Forms/TextArea/styles/mixins.scss +7 -1
- package/src/components/Forms/TextInput/styles/config.scss +3 -1
- package/src/components/Forms/TextInput/styles/mixins.scss +7 -1
- package/src/components/Forms/TextInput/styles/style.scss +17 -12
- package/src/components/Forms/styles/config.scss +3 -2
- package/src/components/Icon/styles/style.scss +2 -1
- package/src/components/Loader/styles/style.scss +0 -2
- package/src/components/Pagination/styles/mixins.scss +8 -6
- package/src/components/Pagination/styles/style.scss +0 -4
- package/src/components/Preview/PreviewGenerator.tsx +15 -2
- package/src/components/PromotionCard/styles/mixins.scss +2 -1
- package/src/components/Section/styles/mixins.scss +27 -5
- package/src/components/SocialButton/styles/config.scss +2 -2
- package/src/components/Stepbar/styles/style.scss +4 -2
- package/src/components/Table/styles/mixins.scss +6 -3
- package/src/components/Tag/styles/config.scss +1 -1
- package/src/components/Tag/styles/style.scss +22 -5
- package/src/components/Tile/Tile.tsx +18 -42
- package/src/components/Tile/styles/mixins.scss +55 -45
- package/src/components/Tile/styles/style.scss +5 -17
- package/src/components/Tile/tests/Tile.unit.test.jsx +9 -78
- package/src/styles/tokens/color-vars.scss +0 -2
- package/src/styles/utilities/color.scss +0 -153
- package/src/components/Tile/CHANGELOG.md +0 -28
- package/src/components/Tile/styles/config.scss +0 -7
package/build/search-index.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"href": "/components/alert",
|
|
16
|
-
"content": "Alert If you need to notify user about something specific, or if you want to explain something shortly, this is the element to do it. Variants Title Title and Description Title and Buttons Prihlásiť sa, Skryť, ]} /> Title, Description and
|
|
16
|
+
"content": "Alert If you need to notify user about something specific, or if you want to explain something shortly, this is the element to do it. Variants Title Title and Description Title and Buttons Prihlásiť sa, Skryť, ]} /> Title, Description and Links Popis toho čo by bolo dobré vysvetliť. Aj na viac riadkov. Niekedy veci nie sú samozrejmé, ale ak sa ich pokúsime vysvetliť, tak sa môžme stretnúť s pochopením. </p> } actionButtons={[ Link Base , Link Base , ]} /> Types There are four types of alert messages you can choose from. - Info - Success - Warning - Danger Info info is default type of alert. It's used for simple, not most important, messages for the user if something happend. }> Success Best feeling is when something went well. You can notify the user about successful things with success type. Warning If you need to warn user about anything use warning type. Danger If warning is not enought and you need to show error message, use danger type. Custom title renderer Alert spacing is quite strict and works 99% of time. In rare cases, the title requires a little more space to breathe. To achieve that, you can render a custom title using the renderTitle prop. ( <h3 className=\"alerttitle mb-medium\">{props.title}</h3> )} description={ } actionButtons={Prihlásiť sa} /> Full width If you need full width Alert, you can use isFullWidth prop. Max width on text remamains the same, because of readability. API React Props | Prop | Type | Default | Description | | --------------- | ---------------------------------------------- | -------- | ---------------------------------------------------------- | | title | string | - | Required.** Main text message | | description | React.ReactNode | - | Description of the alert | | type | \"info\" \\| \"success\" \\| \"warning\" \\| \"danger\" | \"info\" | Type of alert, sets icon and color | | headingLevel | number | 3 | Level of the heading in heading hierarchy (1-6) | | isFullWidth | boolean | - | Sets Alert to full width | | actionButtons | React.ReactNode | - | Additional buttons or links to add interactivity | | renderTitle | (props: AlertProps) => React.ReactNode | - | Custom title renderer. Passes props as function parameter. | | onClose | () => void | - | Callback function when close button is clicked | | className | string | - | Additional CSS classes |"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"href": "/components/anchor-navigation",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"href": "/components/forms/addons",
|
|
96
|
-
"content": "Search Icon Add search icon to TextInput using searchIcon=\"transient\". For Autocomplete, use .input--search-icon through autocompleteConfig.customInputClassName. Icon is visible
|
|
96
|
+
"content": "Search Icon Add search icon to TextInput using searchIcon=\"transient\". For Autocomplete, use .input--search-icon through autocompleteConfig.customInputClassName. Icon is visible in default, focused, filled, disabled and invalid states. It's required to set empty placeholder. Usage with placeholder is described in the example below. Does not apply for radios and checkboxes. <br /> <br /> <br /> Search icon with placeholder When displaying search icon on TextInput with placeholder, use searchIcon=\"persistent\". For Autocomplete, set .input--search-icon-with-placeholder through autocompleteConfig.customInputClassName. Autocomplete with placeholder search icon includes a clear button when it has value. Does not apply for radios and checkboxes. Usage with Autocomplete Disabled autocomplete Usage with Group button Action} control={{ type: \"autocomplete\", id: \"search-group-button\", options: [\"\", \"apple\", \"apricot\", \"banana\"], autocompleteConfig: { customInputClassName: \"input--search-icon-with-placeholder\", placeholder: \"Placeholder text\", }, placeholder: \"\", }} /> Disabled group button Action } control={{ type: \"autocomplete\", id: \"search-group-button-disabled\", options: [\"\", \"apple\", \"apricot\", \"banana\"], autocompleteConfig: { customInputClassName: \"input--search-icon-with-placeholder\", placeholder: \"Placeholder text\", }, placeholder: \"\", isDisabled: true, }} /> Usage with TextInput Regular input Large input Control Group Add prefixes and suffixes to control elements. Does not apply for radios and checkboxes. Example Variants With Button Ok } control={{ type: \"text\", id: \"group-2\" }} /> Size kg</span>, Send]} control={{ type: \"text\", size: \"large\", id: \"group-3\" }} />"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"href": "/components/forms/autocomplete",
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
},
|
|
262
262
|
{
|
|
263
263
|
"href": "/components/tag",
|
|
264
|
-
"content": "Tag
|
|
264
|
+
"content": "Tag Figma groups Tag into three showcases: Tag Interactive, Tag Action Button, and Tag Read Only. transparent is standalone color variant. Tag Interactive Buttons use onClick. Links use href. Button <div> {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} </div> Link <div> {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} </div> Tag Action Button Use actionButton for removable labels. <div> } > {\"Label\"} } > {\"Label\"} } > {\"Label\"} } > {\"Label\"} </div> Tag Read Only Sizes {\"Label\"} {\"Label\"} {\"Label\"} Brand colors {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} {\"Label\"} Semantic colors Info Warning Success Orange Accessibility - Use plain text tags only for non-interactive metadata - Use onClick when tag behaves like button - Use href when tag performs navigation - Always provide visible text that explains tag meaning - Always provide ariaLabel on TagButton - Check contrast when color communicates status API React Props | Name | Type | Default | Description | | -------------- | ------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | color | TagColor | - | Tag color. Options: \"black\", \"orange\", \"yellow\", \"green\", \"blue\", \"success\", \"info\", \"warning\", \"danger\", \"transparent\". | | size | \"small\" \\| \"large\" | - | Tag size. Omit prop for default medium size. | | isDisabled | boolean | false | Disables button state and adds aria-disabled for link, read-only, and action-button variants. | | className | string | - | Additional CSS classes. | | children | React.ReactNode | - | Visible tag label. | | actionButton | React.ReactElement | - | Action button rendered next to label, typically for remove or dismiss flows. | | onClick | React.MouseEventHandler | - | Makes tag render as <button>. | | href | string | - | Makes tag render as <a>. |"
|
|
265
265
|
},
|
|
266
266
|
{
|
|
267
267
|
"href": "/components/testimonial",
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
},
|
|
270
270
|
{
|
|
271
271
|
"href": "/components/tile",
|
|
272
|
-
"content": "Tile Tile
|
|
272
|
+
"content": "Tile Tile grid {Array.from({ length: 7 }).map((, index) => ( <h3 className=\"h5 bold\"> Default label </h3> ))} Tile row {Array.from({ length: 4 }).map((, index) => ( Default label ))} Action variant {Array.from({ length: 3 }).map((_, index) => ( Default label ))} API React Props | Name | Type | Default | Description | | ----------- | -------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------- | | variant | \"compact\" \\| \"action\" | - | Compact reduces spacing, uses row layout on mobile and stacks from sm up. Action adds row layout and chevron. | | hAlign | \"center\" | - | Horizontal alignment of tile content. | | vAlign | \"center\" \\| \"end\" \\| \"space-between\" | - | Vertical alignment of tile content. | | children | React.ReactNode | - | Tile content. | | className | string | - | Additional CSS classes. |"
|
|
273
273
|
},
|
|
274
274
|
{
|
|
275
275
|
"href": "/components/tooltip",
|
|
@@ -389,7 +389,7 @@
|
|
|
389
389
|
},
|
|
390
390
|
{
|
|
391
391
|
"href": "/utilities/color",
|
|
392
|
-
"content": "import Code from \"@/components/Code\"; Color Utilities Utility classes for applying colors based on our semantic design tokens. These classes automatically adapt to light and dark themes. 📖 For complete color system documentation, including theme switching and color philosophy, see our Color Palette & Theme System guide. ⚠️ Always check that your color combinations meet WCAG accessibility guidelines. Token-Based Utility Classes Our color utilities are automatically generated from semantic design tokens. Every class supports both light and dark themes through our .is-light and .is-dark system. Theme Switching Examples All color utilities work with our .is-light and .is-dark theme classes: <div className=\"is-light background-primary text-default p-4 border border-subtle rounded mb-4\"> <h4 className=\"bold\">Light Theme Section</h4> <p className=\"text-secondary\">Secondary text in light theme</p> <button className=\"surface-secondary text-inverse px-3 py-2 rounded\"> Orange Button </button> </div> <div className=\"is-dark background-primary text-default p-4 border border-subtle rounded\"> <h4 className=\"bold\">Dark Theme Section</h4> <p className=\"text-secondary\">Secondary text in dark theme</p> <button className=\"surface-secondary text-inverse px-3 py-2 rounded\"> Orange Button </button> </div> Text Color Utilities Use these classes to apply text colors: - .text-default - Default text color (black in light mode, white in dark mode) - .text-secondary - Secondary text color for less prominent content - .text-disabled - Disabled text color - .text-inverse - Inverse text color (white in light mode, black in dark mode) - .text-accent - Brand accent color (orange) - .text-information - Information/blue text - .text-positive - Success/green text - .text-warning - Warning/yellow text - .text-negative - Error/red text <p className=\"text-default\">Default text color</p> <p className=\"text-secondary\">Secondary text color</p> <p className=\"text-disabled\">Disabled text color</p> <p className=\"background-contrast text-inverse\"> {\"Inverse text color on dark background\"} </p> <p className=\"text-accent\">Accent text color</p> <p className=\"text-information\">Information text color</p> <p className=\"text-positive\">Success text color</p> <p className=\"text-warning\">Warning text color</p> <p className=\"text-negative\">Error text color</p> Icon Color Utilities Use these classes to apply colors to icons: - .icon-default - Default icon color - .icon-inverse - Inverse icon color - .icon-brand - Brand orange color - .icon-accent - Accent color variation - .icon-disabled - Disabled icon color - .icon-information - Information/blue color - .icon-positive - Success/green color - .icon-warning - Warning/yellow color - .icon-negative - Error/red color <span className=\"icon-default\"> </span> <span className=\"icon-inverse\"> </span> <span className=\"icon-brand\"> </span> <span className=\"icon-information\"> </span> <span className=\"icon-positive\"> </span> <span className=\"icon-warning\"> </span> <span className=\"icon-negative\"> </span> Background Utilities Primary Backgrounds - .background-primary - Primary background (white in light mode, dark in dark mode) - .background-secondary - Secondary background for subtle contrast - .background-contrast - High contrast background - .background-accent - Accent background for special content - .background-accent1-blog - Blog accent 1 background - .background-accent2-blog - Blog accent 2 background <p className=\"background-primary\">Primary background</p> <p className=\"background-secondary\">Secondary background</p> <p className=\"background-contrast text-inverse\">Contrast background</p> <p className=\"background-accent\">Accent background</p> <p className=\"background-accent1-blog\">Accent 1 Blog background</p> <p className=\"background-accent2-blog\">Accent 2 Blog background</p> Surface Utilities - .surface-primary - Primary surface - .surface-secondary - Brand orange surface - .surface-tertiary - Dark orange surface - .surface-subtle - Subtle surface for grouping - .surface-moderate - Moderate surface - .surface-contrast - High contrast surface - .surface-accent - Accent surface <p className=\"surface-primary\">Primary surface</p> <p className=\"surface-secondary text-inverse\">Secondary surface</p> <p className=\"surface-tertiary text-inverse\">Tertiary surface</p> <p className=\"surface-subtle\">Subtle surface</p> <p className=\"surface-moderate\">Moderate surface</p> <p className=\"surface-contrast text-inverse\">Contrast surface</p> <p className=\"surface-accent\">Accent surface</p> Fill Utilities - .fill-primary - Primary fill color - .fill-secondary - Secondary fill color (brand orange) - .fill-tertiary - Tertiary fill color (dark orange) - .fill-subtle - Subtle fill color - .fill-moderate - Moderate fill color - .fill-disabled - Disabled fill color - .fill-contrast - Contrast fill color - .fill-accent1 - Blue accent fill - .fill-accent2 - Green accent fill - .fill-accent3 - Pink accent fill - .fill-accent4 - Violet accent fill - .fill-accent5 - Yellow accent fill - .fill-information - Information background - .fill-positive - Success background - .fill-warning - Warning background - .fill-negative - Error background <p className=\"fill-primary\">Primary fill</p> <p className=\"fill-secondary text-inverse\">Secondary fill</p> <p className=\"fill-tertiary text-inverse\">Tertiary fill</p> <p className=\"fill-subtle\">Subtle fill</p> <p className=\"fill-moderate\">Moderate fill</p> <p className=\"fill-disabled\">Disabled fill</p> <p className=\"fill-contrast text-inverse\">Contrast fill</p> <p className=\"fill-accent1 text-inverse\">Blue accent fill</p> <p className=\"fill-accent2 text-inverse\">Green accent fill</p> <p className=\"fill-accent3 text-inverse\">Pink accent fill</p> <p className=\"fill-accent4 text-inverse\">Violet accent fill</p> <p className=\"fill-accent5 text-inverse\">Yellow accent fill</p> <p className=\"fill-information\">Information background</p> <p className=\"fill-positive\">Success background</p> <p className=\"fill-warning\">Warning background</p> <p className=\"fill-negative\">Error background</p> Border Utilities Use these classes to apply border colors: - .border-subtle - Subtle border - .border-strong - Strong border - .border-contrast - High contrast border - .border-accent - Brand accent border - .border-information - Information border - .border-positive - Success border - .border-warning - Warning border - .border-negative - Error border <div className=\"border border-subtle p-4 mb-small\">Subtle border</div> <div className=\"border border-strong p-4 mb-small\">Strong border</div> <div className=\"border border-contrast p-4 mb-small\">Contrast border</div> <div className=\"border border-accent p-4 mb-small\">Accent border</div> <div className=\"border border-information p-4 mb-small\"> Information border </div> <div className=\"border border-positive p-4 mb-small\">Success border</div> <div className=\"border border-warning p-4 mb-small\">Warning border</div> <div className=\"border border-negative p-4\">Error border</div> Legacy Classes (Deprecated) ⚠️ These classes are deprecated and will be removed in future versions. Please use the new token-based classes above. Legacy Text Colors These classes still work but will show deprecation warnings: .color-black → Use .text-default <p>Default text color is black</p> <p className=\"color-black\"> {\"But can be enforced with \"} <code>.color-black</code> {\" if needed. (Deprecated - use .text-default)\"} </p> .color-orange → Use .text-accent <p className=\"color-orange large bold\"> {\"Orange text (Deprecated - use .text-accent)\"} </p> .color-white → Use .text-inverse <p className=\"bg-black color-white\"> {\"White text (Deprecated - use .text-inverse)\"} </p> .color-gray → Use .text-secondary <p className=\"color-gray\">Gray text (Deprecated - use .text-secondary)</p> .color-blue → Use .icon-information <p className=\"color-blue\">Blue text (Deprecated - use .icon-information)</p> .color-danger → Use .icon-negative <p className=\"color-danger\">Danger text (Deprecated - use .icon-negative)</p> Legacy Background Colors These classes still work but will show deprecation warnings: .bg-white → Use .background-primary <p className=\"bg-white\"> {\"Default background (Deprecated - use .background-primary)\"} </p> .bg-black → Use .background-contrast <p className=\"bg-black\"> {\"Black background (Deprecated - use .background-contrast)\"} </p> .bg-orange → Use .surface-secondary <p className=\"bg-orange\"> {\"Orange background (Deprecated - use .surface-secondary)\"} </p> .bg-orange-dark → Use .surface-tertiary <p className=\"bg-orange-dark text-inverse\"> {\"Dark orange background (Deprecated - use .surface-tertiary)\"} </p> .bg-gray → Use .surface-subtle <p className=\"bg-gray\">Gray background (Deprecated - use .surface-subtle)</p> .bg-gray-lighter → Use .background-secondary <p className=\"bg-gray-lighter\"> {\"Light gray background (Deprecated - use .background-secondary)\"} </p> Accent Colors → Use .background- or .fill- <p className=\"bg-accent\"> {\"Accent background (Deprecated - use .background-accent)\"} </p> <p className=\"bg-accent1-blog\"> {\"Accent1 blog (Deprecated - use .background-accent1-blog)\"} </p> <p className=\"bg-accent2-blog\"> {\"Accent2 blog (Deprecated - use .background-accent2-blog)\"} </p> Supporting Colors → Use .fill-accent <p className=\"bg-blue\">Blue background (Deprecated - use .fill-accent1)</p> <p className=\"bg-green\">Green background (Deprecated - use .fill-accent2)</p> <p className=\"bg-pink\">Pink background (Deprecated - use .fill-accent3)</p> <p className=\"bg-violet\"> {\"Violet background (Deprecated - use .fill-accent4)\"} </p> <p className=\"bg-yellow\"> {\"Yellow background (Deprecated - use .fill-accent5)\"} </p> <p className=\"bg-red\">Red background (Deprecated - use .fill-negative)</p> Migration Guide When updating your code, use this mapping: Text Colors - .color-black → .text-default - .color-orange → .text-accent - .color-white → .text-inverse - .color-gray → .text-secondary - .color-blue → .icon-information - .color-danger → .icon-negative Background Colors - .bg-white → .background-primary - .bg-black → .background-contrast - .bg-orange → .surface-secondary - .bg-orange-dark → .surface-tertiary - .bg-gray → .surface-subtle - .bg-gray-lighter → .background-secondary - .bg-blue → .fill-accent1 - .bg-green → .fill-accent2 - .bg-pink → .fill-accent3 - .bg-violet → .fill-accent4 - .bg-yellow → .fill-accent5 - .bg-red → .fill-negative - .bg-accent → .background-accent - .bg-none → .background-none .bg-none → Use .background-none <p className=\"bg-none\">No background (Deprecated - use .background-none)</p> Special Utilities - .background-none - Removes background Best Practices 1. Use semantic names: Choose classes based on meaning rather than visual appearance 2. Consider theme compatibility: All token-based classes automatically adapt to light/dark themes 3. Test accessibility: Always verify color contrast meets WCAG guidelines 4. Migrate gradually: Legacy classes still work but show warnings to help with migration 5. Use border utilities: Apply consistent border colors using the new .border- classes 📖 For detailed guidance on theme switching, CSS variables, and color system architecture, see our Color Palette & Theme System documentation. Related Documentation - Color Palette & Theme System - Complete color system guide - Design Tokens - Token system overview - Accessibility - Color accessibility guidelines"
|
|
392
|
+
"content": "Color Utilities Utility classes for applying colors based on our semantic design tokens. These classes automatically adapt to light and dark themes. 📖 For complete color system documentation, including theme switching and color philosophy, see our Color Palette & Theme System guide. ⚠️ Always check that your color combinations meet WCAG accessibility guidelines. Token-Based Utility Classes Our color utilities are automatically generated from semantic design tokens. Every class supports both light and dark themes through our .is-light and .is-dark system. Theme Switching Examples All color utilities work with our .is-light and .is-dark theme classes: <div className=\"is-light background-primary text-default p-4 border border-subtle rounded mb-4\"> <h4 className=\"bold\">Light Theme Section</h4> <p className=\"text-secondary\">Secondary text in light theme</p> <button className=\"surface-secondary text-inverse px-3 py-2 rounded\"> Orange Button </button> </div> <div className=\"is-dark background-primary text-default p-4 border border-subtle rounded\"> <h4 className=\"bold\">Dark Theme Section</h4> <p className=\"text-secondary\">Secondary text in dark theme</p> <button className=\"surface-secondary text-inverse px-3 py-2 rounded\"> Orange Button </button> </div> Text Color Utilities Use these classes to apply text colors: - .text-default - Default text color (black in light mode, white in dark mode) - .text-secondary - Secondary text color for less prominent content - .text-disabled - Disabled text color - .text-inverse - Inverse text color (white in light mode, black in dark mode) - .text-accent - Brand accent color (orange) <p className=\"text-default\">Default text color</p> <p className=\"text-secondary\">Secondary text color</p> <p className=\"text-disabled\">Disabled text color</p> <p className=\"background-contrast text-inverse\"> {\"Inverse text color on dark background\"} </p> <p className=\"text-accent\">Accent text color</p> Icon Color Utilities Use these classes to apply colors to icons: - .icon-default - Default icon color - .icon-inverse - Inverse icon color - .icon-brand - Brand orange color - .icon-accent - Accent color variation - .icon-disabled - Disabled icon color - .icon-information - Information/blue color - .icon-positive - Success/green color - .icon-warning - Warning/yellow color - .icon-negative - Error/red color <span className=\"icon-default\"> </span> <span className=\"icon-inverse\"> </span> <span className=\"icon-brand\"> </span> <span className=\"icon-information\"> </span> <span className=\"icon-positive\"> </span> <span className=\"icon-warning\"> </span> <span className=\"icon-negative\"> </span> Background Utilities Primary Backgrounds - .background-primary - Primary background (white in light mode, dark in dark mode) - .background-secondary - Secondary background for subtle contrast - .background-contrast - High contrast background - .background-accent - Accent background for special content - .background-accent1-blog - Blog accent 1 background - .background-accent2-blog - Blog accent 2 background <p className=\"background-primary\">Primary background</p> <p className=\"background-secondary\">Secondary background</p> <p className=\"background-contrast text-inverse\">Contrast background</p> <p className=\"background-accent\">Accent background</p> <p className=\"background-accent1-blog\">Accent 1 Blog background</p> <p className=\"background-accent2-blog\">Accent 2 Blog background</p> Surface Utilities - .surface-primary - Primary surface - .surface-secondary - Brand orange surface - .surface-tertiary - Dark orange surface - .surface-subtle - Subtle surface for grouping - .surface-moderate - Moderate surface - .surface-contrast - High contrast surface - .surface-accent - Accent surface <p className=\"surface-primary\">Primary surface</p> <p className=\"surface-secondary text-inverse\">Secondary surface</p> <p className=\"surface-tertiary text-inverse\">Tertiary surface</p> <p className=\"surface-subtle\">Subtle surface</p> <p className=\"surface-moderate\">Moderate surface</p> <p className=\"surface-contrast text-inverse\">Contrast surface</p> <p className=\"surface-accent\">Accent surface</p> Fill Utilities - .fill-primary - Primary fill color - .fill-secondary - Secondary fill color (brand orange) - .fill-tertiary - Tertiary fill color (dark orange) - .fill-subtle - Subtle fill color - .fill-moderate - Moderate fill color - .fill-disabled - Disabled fill color - .fill-contrast - Contrast fill color - .fill-accent1 - Blue accent fill - .fill-accent2 - Green accent fill - .fill-accent3 - Pink accent fill - .fill-accent4 - Violet accent fill - .fill-accent5 - Yellow accent fill - .fill-information - Information background - .fill-positive - Success background - .fill-warning - Warning background - .fill-negative - Error background <p className=\"fill-primary\">Primary fill</p> <p className=\"fill-secondary text-inverse\">Secondary fill</p> <p className=\"fill-tertiary text-inverse\">Tertiary fill</p> <p className=\"fill-subtle\">Subtle fill</p> <p className=\"fill-moderate\">Moderate fill</p> <p className=\"fill-disabled\">Disabled fill</p> <p className=\"fill-contrast text-inverse\">Contrast fill</p> <p className=\"fill-accent1 text-inverse\">Blue accent fill</p> <p className=\"fill-accent2 text-inverse\">Green accent fill</p> <p className=\"fill-accent3 text-inverse\">Pink accent fill</p> <p className=\"fill-accent4 text-inverse\">Violet accent fill</p> <p className=\"fill-accent5 text-inverse\">Yellow accent fill</p> <p className=\"fill-information\">Information background</p> <p className=\"fill-positive\">Success background</p> <p className=\"fill-warning\">Warning background</p> <p className=\"fill-negative\">Error background</p> Border Utilities Use these classes to apply border colors: - .border-subtle - Subtle border - .border-strong - Strong border - .border-contrast - High contrast border - .border-accent - Brand accent border - .border-information - Information border - .border-positive - Success border - .border-warning - Warning border - .border-negative - Error border <div className=\"border border-subtle p-4 mb-small\">Subtle border</div> <div className=\"border border-strong p-4 mb-small\">Strong border</div> <div className=\"border border-contrast p-4 mb-small\">Contrast border</div> <div className=\"border border-accent p-4 mb-small\">Accent border</div> <div className=\"border border-information p-4 mb-small\"> Information border </div> <div className=\"border border-positive p-4 mb-small\">Success border</div> <div className=\"border border-warning p-4 mb-small\">Warning border</div> <div className=\"border border-negative p-4\">Error border</div> Special Utilities - .background-none - Removes background Best Practices 1. Use semantic names: Choose classes based on meaning rather than visual appearance 2. Consider theme compatibility: All token-based classes automatically adapt to light/dark themes 3. Test accessibility: Always verify color contrast meets WCAG guidelines 4. Use background-none: Remove background completely when you need transparent layout 5. Use border utilities: Apply consistent border colors using the new .border- classes 📖 For detailed guidance on theme switching, CSS variables, and color system architecture**, see our Color Palette & Theme System documentation. Related Documentation - Color Palette & Theme System - Complete color system guide - Design Tokens - Token system overview - Accessibility - Color accessibility guidelines"
|
|
393
393
|
},
|
|
394
394
|
{
|
|
395
395
|
"href": "/utilities/flex-alignment",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangesk/orange-design-system",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.49",
|
|
4
4
|
"private": false,
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.x"
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
"classnames": "^2.5.1",
|
|
64
64
|
"daypickr": "^0.3.4",
|
|
65
65
|
"diff2html": "^3.4.56",
|
|
66
|
-
"dompurify": "^3.4.
|
|
67
|
-
"html-react-parser": "6.1.
|
|
66
|
+
"dompurify": "^3.4.6",
|
|
67
|
+
"html-react-parser": "6.1.2",
|
|
68
68
|
"lorem-ipsum": "3.0.0",
|
|
69
69
|
"minisearch": "7.2.0",
|
|
70
70
|
"next": "16.2.6",
|
|
@@ -86,10 +86,10 @@
|
|
|
86
86
|
"wnumb": "^1.2.0"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"@babel/core": "^7.29.
|
|
90
|
-
"@babel/preset-env": "^7.29.
|
|
91
|
-
"@babel/preset-react": "^7.
|
|
92
|
-
"@babel/preset-typescript": "^7.
|
|
89
|
+
"@babel/core": "^7.29.7",
|
|
90
|
+
"@babel/preset-env": "^7.29.7",
|
|
91
|
+
"@babel/preset-react": "^7.29.7",
|
|
92
|
+
"@babel/preset-typescript": "^7.29.7",
|
|
93
93
|
"@biomejs/biome": "latest",
|
|
94
94
|
"@rollup/plugin-alias": "6.0.0",
|
|
95
95
|
"@rollup/plugin-babel": "7.0.0",
|
|
@@ -99,38 +99,38 @@
|
|
|
99
99
|
"@rollup/plugin-terser": "1.0.0",
|
|
100
100
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
101
101
|
"@rollup/plugin-url": "^8.0.2",
|
|
102
|
-
"@rollup/rollup-darwin-arm64": "^4.60.
|
|
102
|
+
"@rollup/rollup-darwin-arm64": "^4.60.4",
|
|
103
103
|
"@testing-library/dom": "^10.4.1",
|
|
104
104
|
"@testing-library/jest-dom": "^6.9.1",
|
|
105
105
|
"@testing-library/react": "^16.3.2",
|
|
106
106
|
"@testing-library/user-event": "^14.6.1",
|
|
107
|
-
"@types/node": "25.
|
|
108
|
-
"@types/react": "19.2.
|
|
107
|
+
"@types/node": "25.9.1",
|
|
108
|
+
"@types/react": "19.2.15",
|
|
109
109
|
"@types/react-dom": "19.2.3",
|
|
110
110
|
"@types/wnumb": "^1.2.3",
|
|
111
|
-
"@vitejs/plugin-react": "6.0.
|
|
112
|
-
"@vitest/browser": "^4.1.
|
|
113
|
-
"@vitest/browser-playwright": "^4.1.
|
|
114
|
-
"@vitest/coverage-v8": "^4.1.
|
|
115
|
-
"@vitest/ui": "^4.1.
|
|
111
|
+
"@vitejs/plugin-react": "6.0.2",
|
|
112
|
+
"@vitest/browser": "^4.1.7",
|
|
113
|
+
"@vitest/browser-playwright": "^4.1.7",
|
|
114
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
115
|
+
"@vitest/ui": "^4.1.7",
|
|
116
116
|
"canvas": "^3.2.3",
|
|
117
117
|
"fs-extra": "^11.3.5",
|
|
118
118
|
"glob": "13.0.6",
|
|
119
|
-
"html-validate": "
|
|
119
|
+
"html-validate": "11.4.0",
|
|
120
120
|
"husky": "^9.1.7",
|
|
121
121
|
"identity-obj-proxy": "^3.0.0",
|
|
122
122
|
"jsdom": "29.1.1",
|
|
123
|
-
"lint-staged": "17.0.
|
|
124
|
-
"playwright": "^1.
|
|
123
|
+
"lint-staged": "17.0.5",
|
|
124
|
+
"playwright": "^1.60.0",
|
|
125
125
|
"prettier": "^3.8.3",
|
|
126
|
-
"rollup": "^4.60.
|
|
126
|
+
"rollup": "^4.60.4",
|
|
127
127
|
"rollup-plugin-copy": "^3.5.0",
|
|
128
128
|
"rollup-plugin-dts": "^6.4.1",
|
|
129
129
|
"rollup-plugin-postcss": "^4.0.2",
|
|
130
|
-
"sass": "^1.
|
|
130
|
+
"sass": "^1.100.0",
|
|
131
131
|
"svg-sprite": "^2.0.4",
|
|
132
132
|
"typescript": "6.0.3",
|
|
133
|
-
"vitest": "^4.1.
|
|
133
|
+
"vitest": "^4.1.7",
|
|
134
134
|
"vitest-axe": "^0.1.0",
|
|
135
135
|
"vitest-browser-react": "^2.2.0"
|
|
136
136
|
},
|
|
@@ -15,19 +15,21 @@
|
|
|
15
15
|
&__indicator.is-indicating:not(.block-action__indicator--color-reset) {
|
|
16
16
|
@include mixins.indicator;
|
|
17
17
|
|
|
18
|
-
.
|
|
18
|
+
.background-primary &,
|
|
19
|
+
.surface-primary & {
|
|
19
20
|
@include mixins.indicator;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
.
|
|
23
|
+
.background-contrast &,
|
|
24
|
+
.surface-contrast & {
|
|
23
25
|
@include mixins.indicator("black");
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
.
|
|
28
|
+
.surface-secondary & {
|
|
27
29
|
@include mixins.indicator("orange");
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
.
|
|
32
|
+
.surface-tertiary & {
|
|
31
33
|
@include mixins.indicator("orange-dark");
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -6,7 +6,7 @@ $card-base: (
|
|
|
6
6
|
background-color: var(--color-surface-primary),
|
|
7
7
|
margin-bottom: space.get("large"),
|
|
8
8
|
border-radius: convert.to-rem(10px),
|
|
9
|
-
border: 1px solid var(--color-border-
|
|
9
|
+
border: 1px solid var(--color-border-subtle),
|
|
10
10
|
overflow: hidden,
|
|
11
11
|
);
|
|
12
12
|
|
|
@@ -31,6 +31,11 @@ import {
|
|
|
31
31
|
SELECTOR_VIEWPORT,
|
|
32
32
|
} from "./constants";
|
|
33
33
|
|
|
34
|
+
interface ExternalControlsState {
|
|
35
|
+
isAtStart: boolean;
|
|
36
|
+
isAtEnd: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
export const defaultConfig: SwiperOptions = {
|
|
35
40
|
navigation: {
|
|
36
41
|
nextEl: SELECTOR_NEXT,
|
|
@@ -94,6 +99,8 @@ export default class Carousel {
|
|
|
94
99
|
track!: HTMLElement;
|
|
95
100
|
instance!: Swiper;
|
|
96
101
|
carouselId?: string;
|
|
102
|
+
private isScrollbarDragging = false;
|
|
103
|
+
private externalControlsState: ExternalControlsState | null = null;
|
|
97
104
|
private resizeObserver?: ResizeObserver;
|
|
98
105
|
private resizeRafId?: number;
|
|
99
106
|
private boundWindowResizeHandler: () => void;
|
|
@@ -215,6 +222,13 @@ export default class Carousel {
|
|
|
215
222
|
slideChangeTransitionEnd: () => {
|
|
216
223
|
this.updateExternalControlsState();
|
|
217
224
|
},
|
|
225
|
+
scrollbarDragStart: () => {
|
|
226
|
+
this.isScrollbarDragging = true;
|
|
227
|
+
},
|
|
228
|
+
scrollbarDragEnd: () => {
|
|
229
|
+
this.isScrollbarDragging = false;
|
|
230
|
+
this.updateExternalControlsState(true);
|
|
231
|
+
},
|
|
218
232
|
},
|
|
219
233
|
});
|
|
220
234
|
|
|
@@ -697,7 +711,7 @@ export default class Carousel {
|
|
|
697
711
|
* Update the disabled state of external navigation controls.
|
|
698
712
|
* Controls are disabled at the start/end of the carousel based on slide position.
|
|
699
713
|
*/
|
|
700
|
-
updateExternalControlsState() {
|
|
714
|
+
updateExternalControlsState(force = false) {
|
|
701
715
|
if (!this.carouselId || !this.instance) {
|
|
702
716
|
return;
|
|
703
717
|
}
|
|
@@ -706,10 +720,22 @@ export default class Carousel {
|
|
|
706
720
|
return;
|
|
707
721
|
}
|
|
708
722
|
|
|
723
|
+
if (this.isScrollbarDragging && !force) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
|
|
709
727
|
const isDisabled = !this.instance.enabled;
|
|
710
728
|
const isAtStart = isDisabled || this.instance.isBeginning;
|
|
711
729
|
const isAtEnd = isDisabled || this.instance.isEnd;
|
|
712
730
|
|
|
731
|
+
this.externalControlsState = { isAtStart, isAtEnd };
|
|
732
|
+
|
|
733
|
+
this.applyExternalControlsState(this.externalControlsState);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
private applyExternalControlsState(state: ExternalControlsState) {
|
|
737
|
+
const { isAtStart, isAtEnd } = state;
|
|
738
|
+
|
|
713
739
|
const prevControls = document.querySelectorAll(
|
|
714
740
|
`[data-carousel-controls="${this.carouselId}"][data-carousel-action="prev"]`,
|
|
715
741
|
);
|
|
@@ -816,6 +842,8 @@ export default class Carousel {
|
|
|
816
842
|
this.instance.destroy();
|
|
817
843
|
}
|
|
818
844
|
|
|
845
|
+
this.isScrollbarDragging = false;
|
|
846
|
+
this.externalControlsState = null;
|
|
819
847
|
this.resizeObserver?.disconnect();
|
|
820
848
|
this.resizeObserver = undefined;
|
|
821
849
|
|
|
@@ -283,6 +283,56 @@ describe("Carousel Static - External Controls", () => {
|
|
|
283
283
|
expect(updateStatesSpy).toHaveBeenCalled();
|
|
284
284
|
}
|
|
285
285
|
});
|
|
286
|
+
|
|
287
|
+
it("should keep external controls disabled while scrollbar drag is active", () => {
|
|
288
|
+
mockSwiperInstance.isBeginning = false;
|
|
289
|
+
mockSwiperInstance.isEnd = true;
|
|
290
|
+
|
|
291
|
+
carouselInstance.updateExternalControlsState();
|
|
292
|
+
|
|
293
|
+
const nextButton = document.getElementById("next-btn");
|
|
294
|
+
expect(nextButton).toHaveAttribute("disabled", "true");
|
|
295
|
+
|
|
296
|
+
const swiperConfig = Swiper.mock.calls[0]?.[1];
|
|
297
|
+
const slideChange = swiperConfig?.on?.slideChange;
|
|
298
|
+
const scrollbarDragStart = swiperConfig?.on?.scrollbarDragStart;
|
|
299
|
+
|
|
300
|
+
scrollbarDragStart?.();
|
|
301
|
+
|
|
302
|
+
expect(nextButton).toHaveAttribute("disabled", "true");
|
|
303
|
+
expect(nextButton).toHaveAttribute("aria-disabled", "true");
|
|
304
|
+
|
|
305
|
+
mockSwiperInstance.isBeginning = false;
|
|
306
|
+
mockSwiperInstance.isEnd = false;
|
|
307
|
+
|
|
308
|
+
slideChange?.();
|
|
309
|
+
|
|
310
|
+
expect(nextButton).toHaveAttribute("disabled", "true");
|
|
311
|
+
expect(nextButton).toHaveAttribute("aria-disabled", "true");
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should sync external controls after scrollbar drag end", () => {
|
|
315
|
+
mockSwiperInstance.isBeginning = false;
|
|
316
|
+
mockSwiperInstance.isEnd = true;
|
|
317
|
+
|
|
318
|
+
carouselInstance.updateExternalControlsState();
|
|
319
|
+
|
|
320
|
+
const nextButton = document.getElementById("next-btn");
|
|
321
|
+
expect(nextButton).toHaveAttribute("disabled", "true");
|
|
322
|
+
|
|
323
|
+
const swiperConfig = Swiper.mock.calls[0]?.[1];
|
|
324
|
+
const scrollbarDragStart = swiperConfig?.on?.scrollbarDragStart;
|
|
325
|
+
const scrollbarDragEnd = swiperConfig?.on?.scrollbarDragEnd;
|
|
326
|
+
|
|
327
|
+
mockSwiperInstance.isBeginning = false;
|
|
328
|
+
mockSwiperInstance.isEnd = false;
|
|
329
|
+
|
|
330
|
+
scrollbarDragStart?.();
|
|
331
|
+
scrollbarDragEnd?.();
|
|
332
|
+
|
|
333
|
+
expect(nextButton).not.toHaveAttribute("disabled");
|
|
334
|
+
expect(nextButton).toHaveAttribute("aria-disabled", "false");
|
|
335
|
+
});
|
|
286
336
|
});
|
|
287
337
|
|
|
288
338
|
describe("Multiple Control Sets", () => {
|
|
@@ -128,6 +128,13 @@ export default class CarouselHero {
|
|
|
128
128
|
? parseInt(this.element.getAttribute("data-interval")!) || 0
|
|
129
129
|
: 0;
|
|
130
130
|
|
|
131
|
+
if (typeof this.config.a11y === "object" && this.config.a11y !== null) {
|
|
132
|
+
this.config.a11y = {
|
|
133
|
+
...this.config.a11y,
|
|
134
|
+
slideRole: this.tabs.length > 0 ? "tabpanel" : "group",
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
131
138
|
if (this.element.hasAttribute("data-swiper-options")) {
|
|
132
139
|
try {
|
|
133
140
|
const customOptions = JSON.parse(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import cx from "classnames";
|
|
4
|
-
import React, { ReactNode } from "react";
|
|
4
|
+
import React, { ReactNode, useId } from "react";
|
|
5
5
|
import { useStatic } from "@/utils/hooks";
|
|
6
6
|
import { Container } from "../Container";
|
|
7
7
|
import { Controls } from "../Controls";
|
|
@@ -21,11 +21,35 @@ import {
|
|
|
21
21
|
CLASS_VIEWPORT_WRAPPER,
|
|
22
22
|
} from "./constants";
|
|
23
23
|
|
|
24
|
+
function getCarouselHeroSlides(children: ReactNode): React.ReactElement[] {
|
|
25
|
+
const slides: React.ReactElement[] = [];
|
|
26
|
+
|
|
27
|
+
React.Children.forEach(children, (child) => {
|
|
28
|
+
if (!React.isValidElement(child)) return;
|
|
29
|
+
|
|
30
|
+
if (child.type === React.Fragment) {
|
|
31
|
+
slides.push(
|
|
32
|
+
...getCarouselHeroSlides(
|
|
33
|
+
(child.props as { children?: ReactNode }).children,
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
slides.push(child);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return slides;
|
|
43
|
+
}
|
|
44
|
+
|
|
24
45
|
interface TabItem {
|
|
25
46
|
label: string;
|
|
47
|
+
id?: string;
|
|
26
48
|
[key: string]: any;
|
|
27
49
|
}
|
|
28
50
|
|
|
51
|
+
type CarouselHeroSlideElement = React.ReactElement<Record<string, any>>;
|
|
52
|
+
|
|
29
53
|
interface CarouselHeroProps {
|
|
30
54
|
className?: string;
|
|
31
55
|
swiperOptions?: Record<string, any>;
|
|
@@ -48,6 +72,24 @@ const CarouselHero: React.FC<CarouselHeroProps> = ({
|
|
|
48
72
|
const [carouselRef] = useStatic(CarouselHeroStatic);
|
|
49
73
|
|
|
50
74
|
const classes = cx(CLASS_ROOT, className);
|
|
75
|
+
const generatedId = `carousel-hero-${useId().replace(/:/g, "-")}`;
|
|
76
|
+
const carouselId =
|
|
77
|
+
typeof other.id === "string" && other.id ? other.id : generatedId;
|
|
78
|
+
const slideItems = getCarouselHeroSlides(
|
|
79
|
+
children,
|
|
80
|
+
) as CarouselHeroSlideElement[];
|
|
81
|
+
const hasTabs = tabs.length > 0 && slideItems.length > 0;
|
|
82
|
+
const getTabId = (tab: TabItem | undefined, index: number) =>
|
|
83
|
+
typeof tab?.id === "string" && tab.id
|
|
84
|
+
? tab.id
|
|
85
|
+
: `${carouselId}-tab-${index}`;
|
|
86
|
+
const getPanelId = (
|
|
87
|
+
slide: CarouselHeroSlideElement | undefined,
|
|
88
|
+
index: number,
|
|
89
|
+
) =>
|
|
90
|
+
slide && typeof slide.props.id === "string" && slide.props.id
|
|
91
|
+
? slide.props.id
|
|
92
|
+
: `${carouselId}-panel-${index}`;
|
|
51
93
|
|
|
52
94
|
const elementClasses = {
|
|
53
95
|
prev: CLASS_PREV,
|
|
@@ -75,6 +117,20 @@ const CarouselHero: React.FC<CarouselHeroProps> = ({
|
|
|
75
117
|
!!(interval && interval >= 1000));
|
|
76
118
|
|
|
77
119
|
const playPauseIcon = "pause";
|
|
120
|
+
const tabSlides = slideItems.map((slide, index) => {
|
|
121
|
+
if (!hasTabs) {
|
|
122
|
+
return slide;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const tabId = getTabId(tabs[index], index);
|
|
126
|
+
const panelId = getPanelId(slide, index);
|
|
127
|
+
|
|
128
|
+
return React.cloneElement(slide as CarouselHeroSlideElement, {
|
|
129
|
+
id: panelId,
|
|
130
|
+
role: "tabpanel",
|
|
131
|
+
"aria-labelledby": tabId,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
78
134
|
|
|
79
135
|
return (
|
|
80
136
|
<div
|
|
@@ -91,21 +147,23 @@ const CarouselHero: React.FC<CarouselHeroProps> = ({
|
|
|
91
147
|
>
|
|
92
148
|
<div className={CLASS_VIEWPORT_WRAPPER}>
|
|
93
149
|
<div className={CLASS_VIEWPORT}>
|
|
94
|
-
<div className={CLASS_TRACK}>{
|
|
150
|
+
<div className={CLASS_TRACK}>{tabSlides}</div>
|
|
95
151
|
</div>
|
|
96
152
|
</div>
|
|
97
153
|
|
|
98
154
|
{/* Controls container */}
|
|
99
155
|
<Container className="d-flex">
|
|
100
156
|
{/* Tab navigation */}
|
|
101
|
-
{
|
|
157
|
+
{hasTabs && (
|
|
102
158
|
<div className={elementClasses.tabs} role="tablist">
|
|
103
159
|
{tabs.map((tab, index) => (
|
|
104
160
|
<button
|
|
161
|
+
id={getTabId(tab, index)}
|
|
105
162
|
key={index}
|
|
106
163
|
type="button"
|
|
107
164
|
className={elementClasses.tab}
|
|
108
165
|
role="tab"
|
|
166
|
+
aria-controls={getPanelId(slideItems[index], index)}
|
|
109
167
|
aria-selected={index === 0 ? "true" : "false"}
|
|
110
168
|
tabIndex={index === 0 ? 0 : -1}
|
|
111
169
|
>
|
|
@@ -156,13 +156,20 @@ describe("rendering CarouselHero", () => {
|
|
|
156
156
|
|
|
157
157
|
it("sets proper ARIA attributes on tabs", () => {
|
|
158
158
|
const { container } = render(
|
|
159
|
-
<CarouselHero tabs={tabs}>
|
|
159
|
+
<CarouselHero id="test-carousel" tabs={tabs}>
|
|
160
|
+
{children}
|
|
161
|
+
</CarouselHero>,
|
|
160
162
|
);
|
|
161
163
|
|
|
162
164
|
const tabButtons = container.querySelectorAll(".carousel-hero__tab");
|
|
163
165
|
|
|
164
166
|
// First tab should be active
|
|
165
167
|
expect(tabButtons[0]).toHaveAttribute("role", "tab");
|
|
168
|
+
expect(tabButtons[0]).toHaveAttribute("id", "test-carousel-tab-0");
|
|
169
|
+
expect(tabButtons[0]).toHaveAttribute(
|
|
170
|
+
"aria-controls",
|
|
171
|
+
"test-carousel-panel-0",
|
|
172
|
+
);
|
|
166
173
|
expect(tabButtons[0]).toHaveAttribute("aria-selected", "true");
|
|
167
174
|
expect(tabButtons[0]).toHaveAttribute("tabIndex", "0");
|
|
168
175
|
|
|
@@ -173,6 +180,29 @@ describe("rendering CarouselHero", () => {
|
|
|
173
180
|
expect(tabButtons[2]).toHaveAttribute("tabIndex", "-1");
|
|
174
181
|
});
|
|
175
182
|
|
|
183
|
+
it("links tabs to corresponding tabpanels", () => {
|
|
184
|
+
const { container } = render(
|
|
185
|
+
<CarouselHero id="test-carousel" tabs={tabs}>
|
|
186
|
+
{children}
|
|
187
|
+
</CarouselHero>,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const slides = container.getElementsByClassName("carousel-hero__slide");
|
|
191
|
+
|
|
192
|
+
expect(slides[0]).toHaveAttribute("role", "tabpanel");
|
|
193
|
+
expect(slides[0]).toHaveAttribute("id", "test-carousel-panel-0");
|
|
194
|
+
expect(slides[0]).toHaveAttribute(
|
|
195
|
+
"aria-labelledby",
|
|
196
|
+
"test-carousel-tab-0",
|
|
197
|
+
);
|
|
198
|
+
expect(slides[1]).toHaveAttribute("role", "tabpanel");
|
|
199
|
+
expect(slides[1]).toHaveAttribute("id", "test-carousel-panel-1");
|
|
200
|
+
expect(slides[1]).toHaveAttribute(
|
|
201
|
+
"aria-labelledby",
|
|
202
|
+
"test-carousel-tab-1",
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
176
206
|
it("shows play/pause button when autoplay is enabled and interval is provided", () => {
|
|
177
207
|
const { container } = render(
|
|
178
208
|
<CarouselHero enableAutoplay interval={3000}>
|