From c41f5edd5c4ede40ef4765b3b85fd166091cb811 Mon Sep 17 00:00:00 2001 From: Adam Kudrna Date: Sat, 31 Aug 2024 15:29:31 +0200 Subject: [PATCH 1/2] Split color prop test by collections --- src/components/Alert/__tests__/Alert.test.jsx | 6 ++++-- src/components/Badge/__tests__/Badge.test.jsx | 6 ++++-- src/components/Button/__tests__/Button.test.jsx | 6 ++++-- src/components/Card/__tests__/Card.test.jsx | 6 ++++-- .../{colorPropTest.js => feedbackColorPropTest.js} | 10 +--------- tests/propTests/neutralColorPropTest.js | 10 ++++++++++ 6 files changed, 27 insertions(+), 17 deletions(-) rename tests/propTests/{colorPropTest.js => feedbackColorPropTest.js} (72%) create mode 100644 tests/propTests/neutralColorPropTest.js diff --git a/src/components/Alert/__tests__/Alert.test.jsx b/src/components/Alert/__tests__/Alert.test.jsx index a846d99a..591cca14 100644 --- a/src/components/Alert/__tests__/Alert.test.jsx +++ b/src/components/Alert/__tests__/Alert.test.jsx @@ -5,7 +5,8 @@ import { within, } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { colorPropTest } from '../../../../tests/propTests/colorPropTest'; +import { feedbackColorPropTest } from '../../../../tests/propTests/feedbackColorPropTest'; +import { neutralColorPropTest } from '../../../../tests/propTests/neutralColorPropTest'; import defaultTranslations from '../../../translations/en'; import { Alert } from '../Alert'; @@ -20,7 +21,8 @@ describe('rendering', () => { { children:
content text
}, (rootElement) => expect(within(rootElement).getByText('content text')), ], - ...colorPropTest, + ...feedbackColorPropTest, + ...neutralColorPropTest, [ { icon: (
icon
) }, (rootElement) => expect(within(rootElement).getByText('icon')), diff --git a/src/components/Badge/__tests__/Badge.test.jsx b/src/components/Badge/__tests__/Badge.test.jsx index 6e40cec2..37531b99 100644 --- a/src/components/Badge/__tests__/Badge.test.jsx +++ b/src/components/Badge/__tests__/Badge.test.jsx @@ -3,7 +3,8 @@ import { render, within, } from '@testing-library/react'; -import { colorPropTest } from '../../../../tests/propTests/colorPropTest'; +import { feedbackColorPropTest } from '../../../../tests/propTests/feedbackColorPropTest'; +import { neutralColorPropTest } from '../../../../tests/propTests/neutralColorPropTest'; import { Badge } from '../Badge'; const mandatoryProps = { @@ -12,7 +13,8 @@ const mandatoryProps = { describe('rendering', () => { it.each([ - ...colorPropTest, + ...feedbackColorPropTest, + ...neutralColorPropTest, [ { label: 'label text' }, (rootElement) => expect(within(rootElement).getByText('label text')), diff --git a/src/components/Button/__tests__/Button.test.jsx b/src/components/Button/__tests__/Button.test.jsx index 17a17491..0a5ee0a0 100644 --- a/src/components/Button/__tests__/Button.test.jsx +++ b/src/components/Button/__tests__/Button.test.jsx @@ -7,7 +7,8 @@ import { import userEvent from '@testing-library/user-event'; import { actionColorPropTest } from '../../../../tests/propTests/actionColorPropTest'; import { blockPropTest } from '../../../../tests/propTests/blockPropTest'; -import { colorPropTest } from '../../../../tests/propTests/colorPropTest'; +import { feedbackColorPropTest } from '../../../../tests/propTests/feedbackColorPropTest'; +import { neutralColorPropTest } from '../../../../tests/propTests/neutralColorPropTest'; import { refPropTest } from '../../../../tests/propTests/refPropTest'; import { labelPropTest } from '../../../../tests/propTests/labelPropTest'; import { sizePropTest } from '../../../../tests/propTests/sizePropTest'; @@ -67,7 +68,8 @@ describe('rendering', () => { ], ...actionColorPropTest, ...blockPropTest, - ...colorPropTest, + ...feedbackColorPropTest, + ...neutralColorPropTest, [ { disabled: true }, (rootElement) => expect(rootElement).toBeDisabled(), diff --git a/src/components/Card/__tests__/Card.test.jsx b/src/components/Card/__tests__/Card.test.jsx index bee505ef..3b902993 100644 --- a/src/components/Card/__tests__/Card.test.jsx +++ b/src/components/Card/__tests__/Card.test.jsx @@ -3,7 +3,8 @@ import { render, within, } from '@testing-library/react'; -import { colorPropTest } from '../../../../tests/propTests/colorPropTest'; +import { feedbackColorPropTest } from '../../../../tests/propTests/feedbackColorPropTest'; +import { neutralColorPropTest } from '../../../../tests/propTests/neutralColorPropTest'; import { raisedPropTest } from '../../../../tests/propTests/raisedPropTest'; import { ScrollView } from '../../ScrollView'; import { Card } from '../Card'; @@ -33,7 +34,8 @@ describe('rendering', () => { { children: scroll view content }, (rootElement) => expect(within(rootElement).getByText('scroll view content')), ], - ...colorPropTest, + ...feedbackColorPropTest, + ...neutralColorPropTest, ...densePropTest('Root'), [ { disabled: true }, diff --git a/tests/propTests/colorPropTest.js b/tests/propTests/feedbackColorPropTest.js similarity index 72% rename from tests/propTests/colorPropTest.js rename to tests/propTests/feedbackColorPropTest.js index 0c47eec4..ff8662ee 100644 --- a/tests/propTests/colorPropTest.js +++ b/tests/propTests/feedbackColorPropTest.js @@ -1,8 +1,4 @@ -export const colorPropTest = [ - [ - { color: 'dark' }, - (rootElement) => expect(rootElement).toHaveClass('isRootColorDark'), - ], +export const feedbackColorPropTest = [ [ { color: 'danger' }, (rootElement) => expect(rootElement).toHaveClass('isRootColorDanger'), @@ -15,10 +11,6 @@ export const colorPropTest = [ { color: 'info' }, (rootElement) => expect(rootElement).toHaveClass('isRootColorInfo'), ], - [ - { color: 'light' }, - (rootElement) => expect(rootElement).toHaveClass('isRootColorLight'), - ], [ { color: 'note' }, (rootElement) => expect(rootElement).toHaveClass('isRootColorNote'), diff --git a/tests/propTests/neutralColorPropTest.js b/tests/propTests/neutralColorPropTest.js new file mode 100644 index 00000000..3d5d294f --- /dev/null +++ b/tests/propTests/neutralColorPropTest.js @@ -0,0 +1,10 @@ +export const neutralColorPropTest = [ + [ + { color: 'dark' }, + (rootElement) => expect(rootElement).toHaveClass('isRootColorDark'), + ], + [ + { color: 'light' }, + (rootElement) => expect(rootElement).toHaveClass('isRootColorLight'), + ], +]; From 34b70522e714b58e3d252d5c8c7817835b770136 Mon Sep 17 00:00:00 2001 From: Adam Kudrna Date: Sat, 31 Aug 2024 16:34:28 +0200 Subject: [PATCH 2/2] Introduce theming of `Badge` color variants `Badge` color variants are now generated using color collections. Other components to be styled the same way already support theming, so `Badge` needs to support theming too. New theming options: `--rui-Badge----__` See `theme.scss` for all available options. --- src/components/Badge/Badge.jsx | 3 +- src/components/Badge/Badge.module.scss | 97 +++++++------------------- src/components/Badge/README.md | 21 +++++- src/components/Badge/_settings.scss | 8 +++ src/styles/tools/_collections.scss | 36 ++++++++++ src/styles/tools/_string.scss | 7 +- src/theme.scss | 64 +++++++++++++++++ 7 files changed, 160 insertions(+), 76 deletions(-) create mode 100644 src/components/Badge/_settings.scss create mode 100644 src/styles/tools/_collections.scss diff --git a/src/components/Badge/Badge.jsx b/src/components/Badge/Badge.jsx index ef841489..653938ff 100644 --- a/src/components/Badge/Badge.jsx +++ b/src/components/Badge/Badge.jsx @@ -32,7 +32,8 @@ Badge.defaultProps = { Badge.propTypes = { /** - * [Color variant](/docs/foundation/colors#component-colors) to clarify importance and meaning of the badge. + * Color to clarify importance and meaning of the badge. Implements + * [Feedback and Neutral color collections](/docs/foundation/collections#colors). */ color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note', 'light', 'dark']), /** diff --git a/src/components/Badge/Badge.module.scss b/src/components/Badge/Badge.module.scss index 1949d937..7e57d903 100644 --- a/src/components/Badge/Badge.module.scss +++ b/src/components/Badge/Badge.module.scss @@ -2,14 +2,14 @@ @use "sass:math"; @use "../../styles/theme/borders"; @use "../../styles/theme/typography"; - -$_badge-size: 1.25rem; +@use "../../styles/tools/collections"; +@use "settings"; @layer components.badge { .root { display: inline-block; - min-width: $_badge-size; - height: $_badge-size; + min-width: settings.$badge-size; + height: settings.$badge-size; padding: 0.25rem 0.35rem; overflow: hidden; font-weight: map.get(typography.$font-weight-values, bold); @@ -19,7 +19,7 @@ $_badge-size: 1.25rem; white-space: nowrap; vertical-align: baseline; color: var(--rui-local-color); - border-radius: math.div($_badge-size, 2); + border-radius: math.div(settings.$badge-size, 2); } .isRootPriorityFilled { @@ -27,45 +27,20 @@ $_badge-size: 1.25rem; box-shadow: var(--rui-local-box-shadow, #{0 0 0 2px rgb(255 255 255 / 80%)}); } - .isRootColorSuccess { - --rui-local-color: var(--rui-color-feedback-on-success); - --rui-local-background-color: var(--rui-color-feedback-success); - } - - .isRootColorWarning { - --rui-local-color: var(--rui-color-feedback-on-warning); - --rui-local-background-color: var(--rui-color-feedback-warning); - } - - .isRootColorDanger { - --rui-local-color: var(--rui-color-feedback-on-danger); - --rui-local-background-color: var(--rui-color-feedback-danger); - } - - .isRootColorHelp { - --rui-local-color: var(--rui-color-feedback-on-help); - --rui-local-background-color: var(--rui-color-feedback-help); - } - - .isRootColorInfo { - --rui-local-color: var(--rui-color-feedback-on-info); - --rui-local-background-color: var(--rui-color-feedback-info); - } - - .isRootColorNote { - --rui-local-color: var(--rui-color-feedback-on-note); - --rui-local-background-color: var(--rui-color-feedback-note); - } - - .isRootColorLight { - --rui-local-color: var(--rui-color-neutral-on-light); - --rui-local-background-color: var(--rui-color-neutral-light); - --rui-local-box-shadow: none; + @each $color in settings.$colors { + @include collections.generate-properties( + $prefix: "rui-", + $component-name: "Badge", + $modifier-name: "priority", + $modifier-value: "filled", + $variant-name: "color", + $variant-value: $color, + $properties: settings.$themeable-properties-filled, + ); } + .isRootColorLight, .isRootColorDark { - --rui-local-color: var(--rui-color-neutral-on-dark); - --rui-local-background-color: var(--rui-color-neutral-dark); --rui-local-box-shadow: none; } @@ -75,35 +50,15 @@ $_badge-size: 1.25rem; border: borders.$width solid currentcolor; } - .isRootPriorityOutline.isRootColorSuccess { - --rui-local-color: var(--rui-color-feedback-success); - } - - .isRootPriorityOutline.isRootColorWarning { - --rui-local-color: var(--rui-color-feedback-warning); - } - - .isRootPriorityOutline.isRootColorDanger { - --rui-local-color: var(--rui-color-feedback-danger); - } - - .isRootPriorityOutline.isRootColorHelp { - --rui-local-color: var(--rui-color-feedback-help); - } - - .isRootPriorityOutline.isRootColorInfo { - --rui-local-color: var(--rui-color-feedback-info); - } - - .isRootPriorityOutline.isRootColorNote { - --rui-local-color: var(--rui-color-feedback-note); - } - - .isRootPriorityOutline.isRootColorLight { - --rui-local-color: var(--rui-color-neutral-light); - } - - .isRootPriorityOutline.isRootColorDark { - --rui-local-color: var(--rui-color-neutral-dark); + @each $color in settings.$colors { + @include collections.generate-properties( + $prefix: "rui-", + $component-name: "Badge", + $modifier-name: "priority", + $modifier-value: "outline", + $variant-name: "color", + $variant-value: $color, + $properties: settings.$themeable-properties-outline, + ); } } diff --git a/src/components/Badge/README.md b/src/components/Badge/README.md index 84f343d2..ceec6fed 100644 --- a/src/components/Badge/README.md +++ b/src/components/Badge/README.md @@ -29,8 +29,9 @@ lowest: 1. filled 2. outline -All priorities come in supported -[component colors](/docs/foundation/colors#component-colors). +All priorities are available in colors from supported +[color collections](/docs/foundation/collections#colors). +Check [API](#api) to see which collections are supported. ### Filled @@ -102,5 +103,21 @@ helps to improve its accessibility. +## Theming + +It's possible to adjust the theme of specific badge color variant. Naming +convention looks as follows: + +`--rui-Badge----__` + +Where: + +- `` is one of `filled` or `outline`, +- `` is a value from supported + [color collections](/docs/foundation/collections#colors) + (check [API](#api) to see which collections are supported), +- `` is one of `color` (color of text) or `background-color` for the + filled priority, or just `color` for the outline priority. + [div-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div#attributes [React common props]: https://react.dev/reference/react-dom/components/common#common-props diff --git a/src/components/Badge/_settings.scss b/src/components/Badge/_settings.scss new file mode 100644 index 00000000..294181de --- /dev/null +++ b/src/components/Badge/_settings.scss @@ -0,0 +1,8 @@ +@use "sass:list"; +@use "../../styles/settings/collections"; + +$badge-size: 1.25rem; + +$colors: list.join(collections.$feedback-colors, collections.$neutral-colors); +$themeable-properties-filled: color, background-color; +$themeable-properties-outline: color; diff --git a/src/styles/tools/_collections.scss b/src/styles/tools/_collections.scss new file mode 100644 index 00000000..2d557398 --- /dev/null +++ b/src/styles/tools/_collections.scss @@ -0,0 +1,36 @@ +@use "string" as rui-string; + +@mixin generate-properties( + $prefix, + $component-name, + $modifier-name: null, + $modifier-value: null, + $variant-name, + $variant-value, + $properties, +) { + $combined-class-name: + if( + $modifier-name and $modifier-value, + ".isRoot#{rui-string.capitalize($modifier-name)}#{rui-string.capitalize($modifier-value)}", + "" + ); + + #{$combined-class-name}.isRoot#{rui-string.capitalize($variant-name)}#{rui-string.capitalize($variant-value)} { + @each $property in $properties { + --#{$prefix}local-#{$property}: + var( + #{ + "--" + + $prefix + + $component-name + + if($modifier-value, "--" + $modifier-value, "") + + "--" + + $variant-value + + "__" + + $property + } + ); + } + } +} diff --git a/src/styles/tools/_string.scss b/src/styles/tools/_string.scss index f0dde40c..b8dc32b9 100644 --- a/src/styles/tools/_string.scss +++ b/src/styles/tools/_string.scss @@ -1,7 +1,10 @@ -// Author: Hugo Giraudel - @use "sass:string"; +@function capitalize($string) { + @return string.to-upper-case(string.slice($string, 1, 1)) + string.slice($string, 2); +} + +// Author: Hugo Giraudel @function replace($string, $search, $replace: "") { $index: string.index($string, $search); diff --git a/src/theme.scss b/src/theme.scss index ae0ebb51..d7537a63 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -306,6 +306,70 @@ --rui-Alert--dark__foreground-color: var(--rui-color-neutral-on-dark); --rui-Alert--dark__background-color: var(--rui-color-background-dark); + // + // Badge + // ===== + + // Badge: filled priority + + // Badge: filled priority: success variant + --rui-Badge--filled--success__color: var(--rui-color-feedback-on-success); + --rui-Badge--filled--success__background-color: var(--rui-color-feedback-success); + + // Badge: filled priority: warning variant + --rui-Badge--filled--warning__color: var(--rui-color-feedback-on-warning); + --rui-Badge--filled--warning__background-color: var(--rui-color-feedback-warning); + + // Badge: filled priority: danger variant + --rui-Badge--filled--danger__color: var(--rui-color-feedback-on-danger); + --rui-Badge--filled--danger__background-color: var(--rui-color-feedback-danger); + + // Badge: filled priority: help variant + --rui-Badge--filled--help__color: var(--rui-color-feedback-on-help); + --rui-Badge--filled--help__background-color: var(--rui-color-feedback-help); + + // Badge: filled priority: info variant + --rui-Badge--filled--info__color: var(--rui-color-feedback-on-info); + --rui-Badge--filled--info__background-color: var(--rui-color-feedback-info); + + // Badge: filled priority: note variant + --rui-Badge--filled--note__color: var(--rui-color-feedback-on-note); + --rui-Badge--filled--note__background-color: var(--rui-color-feedback-note); + + // Badge: filled priority: light variant + --rui-Badge--filled--light__color: var(--rui-color-neutral-on-light); + --rui-Badge--filled--light__background-color: var(--rui-color-neutral-light); + + // Badge: filled priority: dark variant + --rui-Badge--filled--dark__color: var(--rui-color-neutral-on-dark); + --rui-Badge--filled--dark__background-color: var(--rui-color-neutral-dark); + + // Badge: outline priority + + // Badge: outline priority: success variant + --rui-Badge--outline--success__color: var(--rui-color-feedback-success); + + // Badge: outline priority: warning variant + --rui-Badge--outline--warning__color: var(--rui-color-feedback-warning); + + // Badge: outline priority: danger variant + --rui-Badge--outline--danger__color: var(--rui-color-feedback-danger); + + // Badge: outline priority: help variant + --rui-Badge--outline--help__color: var(--rui-color-feedback-help); + + // Badge: outline priority: info variant + --rui-Badge--outline--info__color: var(--rui-color-feedback-info); + + // Badge: outline priority: note variant + --rui-Badge--outline--note__color: var(--rui-color-feedback-note); + + // Badge: outline priority: light variant + --rui-Badge--outline--light__color: var(--rui-color-neutral-light); + + // Badge: outline priority: dark variant + --rui-Badge--outline--dark__color: var(--rui-color-neutral-dark); + // // Button // ======