From 0bbacf0b1f9c6e7a99106a93aef0b37fe4032f84 Mon Sep 17 00:00:00 2001 From: Constance Date: Tue, 3 May 2022 11:13:25 -0700 Subject: [PATCH] [Emotion] Convert Sass typography mixins to JS (#5854) * Add unit tests for euiFontSize mixin * Convert euiTextBreakWord to function + add unit test * [misc] fix downstream mounted snapshot by changing to render * Convert euiTextTruncate mixin from Sass to CSS-in-JS + add optional arg for max-width property * Convert euiNumberFormat mixin from Sass to CSS-in-JS * Convert euiTextShift from Sass to CSS-in-JS + move to `functions` rather than `mixin` - per feedback from Caroline, this util is mostly used internally and does not need to be publicly exported/documented * [Docs] Move text utilities to their own page - a la https://elastic.github.io/eui/#/utilities/scroll * [Docs] Split text utilities into sections + add mixin snippets * [Docs] Add prop to ThemeExample that allows stacking/removing margin-bottom - to group a class utility and its mixin together visually * [PR feedback] Remove margin-bottom/stacking behavior - Caroline will style page later This reverts commit 9fe7b65b87e3f78db941ab4c6fbb91a329bb8b41. * [PR feedback] Documentation copy + example fixes * [PR feedback] Comment copy, remove IE workaround * [PR feedback] DRY out unit measurement tests * [PR feedback] Fix euiNumberFormat to use `euiTheme.font.featureSettings` + add hook version, since this util now references euiTheme * [PR feedback] Add callouts to CSS utilities docs page * [PR feedback] Tweak text align documentation * [PR feedback] Document euiTextTruncate parameters in title Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> * Add changelog entry Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> --- src-docs/src/routes.js | 3 + src-docs/src/views/text_utilities/align.tsx | 62 +++++ src-docs/src/views/text_utilities/color.tsx | 32 +++ src-docs/src/views/text_utilities/numbers.tsx | 88 ++++++ .../text_utilities/text_utilities_example.js | 45 +++ .../src/views/text_utilities/wrapping.tsx | 202 ++++++++++++++ .../utility_classes_example.js | 43 +-- .../utility_classes/utility_classes_text.js | 261 ------------------ .../card/__snapshots__/card.test.tsx.snap | 150 ++-------- src/components/card/card.test.tsx | 2 +- src/components/title/title.styles.ts | 2 +- src/global_styling/functions/typography.ts | 24 ++ .../__snapshots__/_typography.test.ts.snap | 201 ++++++++++++++ src/global_styling/mixins/_typography.test.ts | 58 ++++ src/global_styling/mixins/_typography.ts | 31 ++- upcoming_changelogs/5854.md | 1 + 16 files changed, 791 insertions(+), 414 deletions(-) create mode 100644 src-docs/src/views/text_utilities/align.tsx create mode 100644 src-docs/src/views/text_utilities/color.tsx create mode 100644 src-docs/src/views/text_utilities/numbers.tsx create mode 100644 src-docs/src/views/text_utilities/text_utilities_example.js create mode 100644 src-docs/src/views/text_utilities/wrapping.tsx delete mode 100644 src-docs/src/views/utility_classes/utility_classes_text.js create mode 100644 src/global_styling/mixins/__snapshots__/_typography.test.ts.snap create mode 100644 src/global_styling/mixins/_typography.test.ts create mode 100644 upcoming_changelogs/5854.md diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 654cd452be8..6815cf9a03c 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -220,6 +220,8 @@ import { TextDiffExample } from './views/text_diff/text_diff_example'; import { TextExample } from './views/text/text_example'; +import { TextUtilitiesExample } from './views/text_utilities/text_utilities_example'; + import { TimelineExample } from './views/timeline/timeline_example'; import { TitleExample } from './views/title/title_example'; @@ -598,6 +600,7 @@ const navigation = [ ResponsiveExample, ScrollExample, TextDiffExample, + TextUtilitiesExample, WindowEventExample, ].map((example) => createExample(example)), }, diff --git a/src-docs/src/views/text_utilities/align.tsx b/src-docs/src/views/text_utilities/align.tsx new file mode 100644 index 00000000000..4db5a657fe4 --- /dev/null +++ b/src-docs/src/views/text_utilities/align.tsx @@ -0,0 +1,62 @@ +import React from 'react'; + +import { EuiMark } from '../../../../src'; +import { ThemeExample } from '../theme/_components/_theme_example'; + +export default () => ( + <> + .eui-textLeft} + description={ +

+ Changes the element’s text alignment to the left/starting side of its + container. +

+ } + example={ +
+ Left align text +
+ } + snippet={`
+ /* Your content */ +
`} + /> + + .eui-textCenter} + description={ +

+ Changes the element’s text alignment to the center/middle of its + container. +

+ } + example={ +
+ Center align text +
+ } + snippet={`
+ /* Your content */ +
`} + /> + + .eui-textRight} + description={ +

+ Changes the element’s text alignment to the right/ending side of its + container. +

+ } + example={ +
+ Right align text +
+ } + snippet={`
+ /* Your content */ +
`} + /> + +); diff --git a/src-docs/src/views/text_utilities/color.tsx b/src-docs/src/views/text_utilities/color.tsx new file mode 100644 index 00000000000..f9dfb03432f --- /dev/null +++ b/src-docs/src/views/text_utilities/color.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { EuiCode, EuiTextColor } from '../../../../src'; +import { ThemeExample } from '../theme/_components/_theme_example'; + +export default () => ( + .eui-textInheritColor} + description={ + <> +

Forces the component to inherit its text color from its parent.

+

+ For changing the color of your text to on of the named colors,{' '} + + use EuiText or EuiTextColor + + . +

+ + } + example={ + + I am code that + matches the EuiTextColor + + } + snippet={` + I am danger code +`} + /> +); diff --git a/src-docs/src/views/text_utilities/numbers.tsx b/src-docs/src/views/text_utilities/numbers.tsx new file mode 100644 index 00000000000..1678f71c115 --- /dev/null +++ b/src-docs/src/views/text_utilities/numbers.tsx @@ -0,0 +1,88 @@ +import React, { useContext } from 'react'; + +import { ThemeContext } from '../../components/with_theme'; + +import { + EuiCode, + EuiTextAlign, + EuiFlexGrid, + EuiFlexItem, +} from '../../../../src'; +import { ThemeExample } from '../theme/_components/_theme_example'; + +export default () => { + const themeContext = useContext(ThemeContext); + const currentLanguage = themeContext.themeLanguage; + const showSass = currentLanguage.includes('sass'); + + return ( + <> + .eui-textNumber} + description={ +

+ Applies{' '} + + {'font-feature-settings: "tnum";'} + {' '} + so that numbers align more properly in a column, especially when + right aligned. +

+ } + example={ + + + +

+ Without class +
+ 11317.11 +
+ 0040.900 +

+
+ +

+ With class +
+ 11317.11 +
+ 0040.900 +

+
+
+
+ } + snippet={`
+ /* Your number content */ +
`} + /> + {/* Mixin */} + {!showSass ? ( + useEuiNumberFormat()} + description={ +

+ Use this style function to apply number text styles within your + CSS-in-JS styling. No parameters are taken for this utility. +

+ } + snippet={'${useEuiNumberFormat()}'} + snippetLanguage="emotion" + /> + ) : ( + @include euiNumberFormat} + description={ +

+ Use this Sass mixin to apply number text styles to your selectors. + No parameters are taken for this utility. +

+ } + snippet={'@include euiNumberFormat;'} + snippetLanguage="scss" + /> + )} + + ); +}; diff --git a/src-docs/src/views/text_utilities/text_utilities_example.js b/src-docs/src/views/text_utilities/text_utilities_example.js new file mode 100644 index 00000000000..27260b29d97 --- /dev/null +++ b/src-docs/src/views/text_utilities/text_utilities_example.js @@ -0,0 +1,45 @@ +import React from 'react'; + +import { EuiText } from '../../../../src/components'; + +import TextAlignUtilities from './align'; +import TextWrappingUtilities from './wrapping'; +import TextNumberUtilities from './numbers'; +import TextColorUtilities from './color'; + +export const TextUtilitiesExample = { + title: 'Text', + showThemeLanguageToggle: true, + intro: ( + +

+ These text utilities are available primarily as CSS classes to aid in + quickly styling your text. Some utilities are additionally available as + either CSS-in-JS or Sass mixins to optionally compose within your own + custom styles. +

+
+ ), + sections: [ + { + title: 'Alignment', + wrapText: false, + text: , + }, + { + title: 'Wrapping', + wrapText: false, + text: , + }, + { + title: 'Numbers', + wrapText: false, + text: , + }, + { + title: 'Color', + wrapText: false, + text: , + }, + ], +}; diff --git a/src-docs/src/views/text_utilities/wrapping.tsx b/src-docs/src/views/text_utilities/wrapping.tsx new file mode 100644 index 00000000000..794158279b5 --- /dev/null +++ b/src-docs/src/views/text_utilities/wrapping.tsx @@ -0,0 +1,202 @@ +import React, { useContext } from 'react'; + +import { ThemeContext } from '../../components/with_theme'; + +import { EuiPanel, EuiCode } from '../../../../src'; +import { ThemeExample } from '../theme/_components/_theme_example'; + +const maxWidth = 300; +const longLink = + 'http://www.hithereimalongurl.com/dave_will_just_ramble_on_in_a_long_sentence_like_this/?ok=cool'; + +export default () => { + const themeContext = useContext(ThemeContext); + const currentLanguage = themeContext.themeLanguage; + const showSass = currentLanguage.includes('sass'); + + return ( + <> + .eui-textNoWrap} + description={

Forces text not to wrap even in small containers.

} + example={ + + This text will not to wrap but extend beyond the boundaries of the + yellow box. + + } + snippet={`
+ /* Your content */ +
`} + /> + + .eui-textTruncate} + description={ + <> +

+ Truncates text at 100% width of its parent and will display an + ellipsis. +

+

+ Tip: When truncating text, it is recommended to + include the full text within an HTML title{' '} + attribute or by wrapping the element within an{' '} + EuiToolTip. +

+ + } + example={ + + This text will not to wrap but truncate beyond the boundaries of the + yellow box. + + } + snippet={`
+ /* Your content */ +
`} + /> + {/* Mixin */} + {!showSass ? ( + euiTextTruncate(maxWidth?)} + description={ + <> +

+ Use this style function to apply truncation within your + CSS-in-JS styling. +

+

+ This utility accepts a single optional parameter for customizing + the maximum width of the truncated text. If not passed, it + defaults to max-width: 100%;. +

+ + } + snippet={'${euiTextTruncate()}'} + snippetLanguage="emotion" + /> + ) : ( + include euiTextTruncate} + description={ +

+ Use this Sass mixin to apply truncation to your selectors. No + parameters are taken for this utility. +

+ } + snippet={'@include euiTextTruncate;'} + snippetLanguage="scss" + /> + )} + + .eui-textBreakWord} + description={ +

+ Wraps the text across lines like normal, but forces long words like + URLs to break. +

+ } + example={ + + This text will wrap like normal but this long link {longLink} will + break mid-word. + + } + snippet={`
+ /* Your content */ +
`} + /> + {/* Mixin */} + {!showSass ? ( + euiTextBreakWord()} + description={ +

+ Use this style function to apply break-word styles within your + CSS-in-JS styling. No parameters are taken for this utility. +

+ } + snippet={'${euiTextBreakWord()}'} + snippetLanguage="emotion" + /> + ) : ( + @include euiTextBreakWord} + description={ +

+ Use this Sass mixin to apply break-word styling to your selectors. + No parameters are taken for this utility. +

+ } + snippet={'@include euiTextBreakWord;'} + snippetLanguage="scss" + /> + )} + + .eui-textBreakAll} + description={ +

+ Wraps the text across lines always forcing the last word on the line + to break. +

+ } + example={ + + This text block will wrap, breaking up anything including long URLs{' '} + {longLink} and run on strings like this + --------------------------------------------------------------------------. + + } + snippet={`
+ /* Your content */ +
`} + /> + + .eui-textBreakNormal} + description={ +

+ Reverts the text back to the normal wrapping scheme of not forcing + word breaks. +

+ } + example={ + + This text block will wrap normally, but will not break long URLs{' '} + {longLink} but may break run on strings like this + ---------------------------------------------------------------. + + } + snippet={`
+ /* Your content */ +
`} + /> + + ); +}; diff --git a/src-docs/src/views/utility_classes/utility_classes_example.js b/src-docs/src/views/utility_classes/utility_classes_example.js index 85978bfdbc5..2c7a73045b8 100644 --- a/src-docs/src/views/utility_classes/utility_classes_example.js +++ b/src-docs/src/views/utility_classes/utility_classes_example.js @@ -1,24 +1,37 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { EuiSpacer, EuiText } from '../../../../src/components'; -import { EuiHorizontalRule } from '../../../../src/components/horizontal_rule'; +import { + EuiSpacer, + EuiText, + EuiCallOut, + EuiHorizontalRule, +} from '../../../../src/components'; import UtilityClassesDisplay from './utility_classes'; -import UtilityClassesText from './utility_classes_text'; import UtilityClassesVertAlign from './utility_classes_vert_align'; import UtilityClassesResponsive from './utility_classes_responsive'; export const UtilityClassesExample = { title: 'CSS utility classes', intro: ( - -

- The following CSS-only classes are provided as helper utilities. They - are useful for making micro-adjustments to existing React components. + <> + +

+ The following CSS-only classes are provided as helper utilities. They + are useful for making micro-adjustments to existing React components. +

+
+ + For overflow and scrolling specific utilities, go to the{' '} Scroll documentation page. -

- +
+ + + For text and typography specific utilities, go to the{' '} + Text documentation page. + + ), sections: [ { @@ -33,18 +46,6 @@ export const UtilityClassesExample = { ), }, - { - title: 'Text', - wrapText: false, - text: ( - <> - - - - - - ), - }, { title: 'Vertical alignment', wrapText: false, diff --git a/src-docs/src/views/utility_classes/utility_classes_text.js b/src-docs/src/views/utility_classes/utility_classes_text.js deleted file mode 100644 index d5e54b027f1..00000000000 --- a/src-docs/src/views/utility_classes/utility_classes_text.js +++ /dev/null @@ -1,261 +0,0 @@ -import React from 'react'; - -import { - EuiCode, - EuiSpacer, - EuiTextColor, - EuiMark, - EuiFlexGrid, - EuiFlexItem, - EuiPanel, - EuiTextAlign, -} from '../../../../src/components'; -import { UtilityClassesSection } from './utility_classes_section'; - -const longLink = - 'http://www.hithereimalongurl.com/dave_will_just_ramble_on_in_a_long_sentence_like_this/?ok=cool'; - -const wrappingDivExampleStyle = { - maxWidth: 300, -}; - -export default () => ( - <> - -

Forces the component to inherit its text color from its parent.

-

- For changing the color of your text to on of the named colors, use{' '} - EuiText or EuiTextColor. -

- - } - example={ - - I am code that - matches the EuiTextColor - - } - snippet={` - I am danger code -`} - /> - - - Changes the element’s text alignment property to{' '} - text-align: left; -

- } - example={ -
- Left align text -
- } - snippet={`
- /* Your content */ -
`} - /> - - - Changes the element’s text alignment property to{' '} - text-align: center; -

- } - example={ -
- Center align text -
- } - snippet={`
- /* Your content */ -
`} - /> - - - Changes the element’s text alignment property to{' '} - text-align: right; -

- } - example={ -
- Right align text -
- } - snippet={`
- /* Your content */ -
`} - /> - - Forces text not to wrap even in small containers.

} - example={ - - This text will not to wrap but extend beyond the boundaries of the - yellow box. - - } - snippet={`
- /* Your content */ -
`} - /> - - -

- Truncates text at 100% width of its parent and will display an - ellipsis. -

-

- Tip: When truncating text, it is recommended to - include the full text within an HTML title{' '} - attribute or by wrapping the element within an{' '} - EuiToolTip. -

- - } - example={ - - This text will not to wrap but truncate beyond the boundaries of the - yellow box. - - } - snippet={`
- /* Your content */ -
`} - /> - - - Wraps the text across lines like normal, but forces long words like - {" URL's"} to break. -

- } - example={ - - This text will wrap like normal but this long link {longLink} will - break mid-word. - - } - snippet={`
- /* Your content */ -
`} - /> - - - Wraps the text across lines always forcing the last word on the line - to break. -

- } - example={ - - This text block will wrap, breaking up anything including long{' '} - {"URL's"} {longLink} and run on strings like this - --------------------------------------------------------------------------. - - } - snippet={`
- /* Your content */ -
`} - /> - - - Reverts the text back to the normal wrapping scheme of not forcing - word breaks. -

- } - example={ - - This text block will wrap normally, but will not break long {"URL's"}{' '} - {longLink} but may break run on strings like this - ---------------------------------------------------------------. - - } - snippet={`
- /* Your content */ -
`} - /> - - - Applies{' '} - {'font-feature-settings: "tnum";'}{' '} - so that numbers align more properly in a column, especially when right - aligned. -

- } - example={ - - - -

- Without class -
- 11317.11 -
- 0040.900 -

-
- -

- With class -
- 11317.11 -
- 0040.900 -

-
-
-
- } - snippet={`
- /* Your number content */ -
`} - /> - -); diff --git a/src/components/card/__snapshots__/card.test.tsx.snap b/src/components/card/__snapshots__/card.test.tsx.snap index a5c84ee49f9..e7761b33345 100644 --- a/src/components/card/__snapshots__/card.test.tsx.snap +++ b/src/components/card/__snapshots__/card.test.tsx.snap @@ -493,137 +493,35 @@ exports[`EuiCard props horizontal 1`] = ` `; exports[`EuiCard props href supports href as a link 1`] = ` - - -
-
- - - - - - , - "ctr": 2, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "tags": Array [ - , - , - ], - }, - } - } - isStringTag={true} - serialized={ - Object { - "map": "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRpdGxlLnN0eWxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFxRlEiLCJmaWxlIjoidGl0bGUuc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCBFbGFzdGljc2VhcmNoIEIuVi4gYW5kL29yIGxpY2Vuc2VkIHRvIEVsYXN0aWNzZWFyY2ggQi5WLiB1bmRlciBvbmVcbiAqIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiBMaWNlbnNlZCB1bmRlciB0aGUgRWxhc3RpYyBMaWNlbnNlXG4gKiAyLjAgYW5kIHRoZSBTZXJ2ZXIgU2lkZSBQdWJsaWMgTGljZW5zZSwgdiAxOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdFxuICogaW4gY29tcGxpYW5jZSB3aXRoLCBhdCB5b3VyIGVsZWN0aW9uLCB0aGUgRWxhc3RpYyBMaWNlbnNlIDIuMCBvciB0aGUgU2VydmVyXG4gKiBTaWRlIFB1YmxpYyBMaWNlbnNlLCB2IDEuXG4gKi9cblxuaW1wb3J0IHsgQ1NTUHJvcGVydGllcyB9IGZyb20gJ3JlYWN0JztcbmltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0JztcbmltcG9ydCB7IFVzZUV1aVRoZW1lLCB1c2VFdWlUaGVtZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzJztcbmltcG9ydCB7XG4gIGV1aVRleHRCcmVha1dvcmQsXG4gIGV1aUZvbnRTaXplLFxuICBfRXVpVGhlbWVGb250U2NhbGUsXG4gIF9FdWlUaGVtZUZvbnRTaXplTWVhc3VyZW1lbnQsXG59IGZyb20gJy4uLy4uL2dsb2JhbF9zdHlsaW5nJztcbmltcG9ydCB7IEV1aVRpdGxlU2l6ZSB9IGZyb20gJy4vdGl0bGUnO1xuXG4vKipcbiAqIE1peGluXG4gKi9cbnR5cGUgRXVpVGhlbWVUaXRsZSA9IHtcbiAgZm9udFNpemU6IENTU1Byb3BlcnRpZXNbJ2ZvbnRTaXplJ107XG4gIGxpbmVIZWlnaHQ6IENTU1Byb3BlcnRpZXNbJ2xpbmVIZWlnaHQnXTtcbiAgZm9udFdlaWdodDogQ1NTUHJvcGVydGllc1snZm9udFdlaWdodCddO1xuICBjb2xvcjogQ1NTUHJvcGVydGllc1snY29sb3InXTtcbn07XG5cbmV4cG9ydCBjb25zdCBldWlUaXRsZSA9IChcbiAgc2NhbGU6IEV1aVRpdGxlU2l6ZSA9ICdtJyxcbiAgZXVpVGhlbWU6IFVzZUV1aVRoZW1lWydldWlUaGVtZSddLFxuICBtZWFzdXJlbWVudDogX0V1aVRoZW1lRm9udFNpemVNZWFzdXJlbWVudCA9ICdyZW0nXG4pOiBFdWlUaGVtZVRpdGxlID0+IHtcbiAgY29uc3QgdGl0bGVTY2FsZVRvRm9udFNpemVTY2FsZU1hcDoge1xuICAgIFtzaXplIGluIEV1aVRpdGxlU2l6ZV06IF9FdWlUaGVtZUZvbnRTY2FsZTtcbiAgfSA9IHtcbiAgICB4eHhzOiAneHMnLFxuICAgIHh4czogJ3MnLFxuICAgIHhzOiAnbScsXG4gICAgczogJ2wnLFxuICAgIG06ICd4bCcsXG4gICAgbDogJ3h4bCcsXG4gIH07XG5cbiAgcmV0dXJuIHtcbiAgICAuLi5ldWlGb250U2l6ZSh0aXRsZVNjYWxlVG9Gb250U2l6ZVNjYWxlTWFwW3NjYWxlXSwgZXVpVGhlbWUsIG1lYXN1cmVtZW50KSxcbiAgICBmb250V2VpZ2h0OiBldWlUaGVtZS5mb250LndlaWdodFtldWlUaGVtZS5mb250LnRpdGxlLndlaWdodF0sXG4gICAgY29sb3I6IGV1aVRoZW1lLmNvbG9ycy50aXRsZSxcbiAgfTtcbn07XG5cbi8vIEhvb2sgdmVyc2lvblxuZXhwb3J0IGNvbnN0IHVzZUV1aVRpdGxlID0gKFxuICBzY2FsZTogRXVpVGl0bGVTaXplID0gJ20nLFxuICBtZWFzdXJlbWVudDogX0V1aVRoZW1lRm9udFNpemVNZWFzdXJlbWVudCA9ICdyZW0nXG4pOiBFdWlUaGVtZVRpdGxlID0+IHtcbiAgY29uc3QgeyBldWlUaGVtZSB9ID0gdXNlRXVpVGhlbWUoKTtcbiAgcmV0dXJuIGV1aVRpdGxlKHNjYWxlLCBldWlUaGVtZSwgbWVhc3VyZW1lbnQpO1xufTtcblxuLyoqXG4gKiBTdHlsZXNcbiAqL1xuZXhwb3J0IGNvbnN0IGV1aVRpdGxlU3R5bGVzID0gKHsgZXVpVGhlbWUgfTogVXNlRXVpVGhlbWUpID0+ICh7XG4gIGV1aVRpdGxlOiBjc3NgXG4gICAgJHtldWlUZXh0QnJlYWtXb3JkfVxuXG4gICAgJiArICYge1xuICAgICAgbWFyZ2luLXRvcDogJHtldWlUaGVtZS5zaXplLmx9O1xuICAgIH1cbiAgYCxcbiAgdXBwZXJjYXNlOiBjc3NgXG4gICAgdGV4dC10cmFuc2Zvcm06IHVwcGVyY2FzZTtcbiAgYCxcbiAgLy8gU2l6ZXNcbiAgeHh4czogY3NzYFxuICAgICR7ZXVpVGl0bGUoJ3h4eHMnLCBldWlUaGVtZSl9XG4gIGAsXG4gIHh4czogY3NzYFxuICAgICR7ZXVpVGl0bGUoJ3h4cycsIGV1aVRoZW1lKX1cbiAgYCxcbiAgeHM6IGNzc2BcbiAgICAke2V1aVRpdGxlKCd4cycsIGV1aVRoZW1lKX1cbiAgYCxcbiAgczogY3NzYFxuICAgICR7ZXVpVGl0bGUoJ3MnLCBldWlUaGVtZSl9XG4gIGAsXG4gIG06IGNzc2BcbiAgICAke2V1aVRpdGxlKCdtJywgZXVpVGhlbWUpfVxuICBgLFxuICBsOiBjc3NgXG4gICAgJHtldWlUaXRsZSgnbCcsIGV1aVRoZW1lKX1cbiAgYCxcbn0pO1xuIl19 */", - "name": "1mr6cet-euiTitle-s", - "next": undefined, - "styles": "; - overflow-wrap: break-word !important; // makes sure the long string will wrap and not bust out of the container - word-wrap: break-word !important; // spec says, they are literally just alternate names for each other but some browsers support one and not the other - word-break: break-word; // IE doesn't understand but that's ok - &+&{margin-top:24px;};label:euiTitle;;;;font-size:1.5714rem;line-height:1.7143rem;font-weight:700;color:#1a1c21;;;label:s;;;;", - "toString": [Function], - } - } - /> - - - Hoi - - - - - -
-

- There -

-
-
-
+ Hoi + + +
+

+ There +

- - +
+ `; exports[`EuiCard props icon 1`] = ` diff --git a/src/components/card/card.test.tsx b/src/components/card/card.test.tsx index 51f26fc9e84..731293dc709 100644 --- a/src/components/card/card.test.tsx +++ b/src/components/card/card.test.tsx @@ -95,7 +95,7 @@ describe('EuiCard', () => { describe('href', () => { it('supports href as a link', () => { - const component = mount( + const component = render( ); diff --git a/src/components/title/title.styles.ts b/src/components/title/title.styles.ts index 4eb2cefb3e8..b5f206b9f6c 100644 --- a/src/components/title/title.styles.ts +++ b/src/components/title/title.styles.ts @@ -64,7 +64,7 @@ export const useEuiTitle = ( */ export const euiTitleStyles = ({ euiTheme }: UseEuiTheme) => ({ euiTitle: css` - ${euiTextBreakWord} + ${euiTextBreakWord()} & + & { margin-top: ${euiTheme.size.l}; diff --git a/src/global_styling/functions/typography.ts b/src/global_styling/functions/typography.ts index 5f07a0c4408..7b04b734d26 100644 --- a/src/global_styling/functions/typography.ts +++ b/src/global_styling/functions/typography.ts @@ -9,6 +9,7 @@ import { _EuiThemeFontScale, _EuiThemeFontSizeMeasurement, + _EuiThemeFontWeights, } from '../variables/typography'; import { UseEuiTheme } from '../../services/theme/hooks'; @@ -75,3 +76,26 @@ export function euiLineHeightFromBaseline( ? `${pixelValue}px` : `${(pixelValue / denominator).toFixed(4)}rem`; } + +/** + * Text weight shifting + * + * When changing the font-weight based on the state of the component, + * this mixin will ensure that the sizing is dependent on the boldest + * weight so it doesn't shift sibling content. + */ +export const euiTextShift = ( + fontWeight: keyof _EuiThemeFontWeights = 'bold', + attribute: string = 'data-text', + euiTheme: UseEuiTheme['euiTheme'] +) => { + return ` + &::after { + display: block; + content: attr(${attribute}); + font-weight: ${euiTheme.font.weight[fontWeight]}; + height: 0; + overflow: hidden; + visibility: hidden; + }`; +}; diff --git a/src/global_styling/mixins/__snapshots__/_typography.test.ts.snap b/src/global_styling/mixins/__snapshots__/_typography.test.ts.snap new file mode 100644 index 00000000000..b6a4d45d616 --- /dev/null +++ b/src/global_styling/mixins/__snapshots__/_typography.test.ts.snap @@ -0,0 +1,201 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`euiFontSize returns an object of font-size and line-height for each scale em l 1`] = ` +Object { + "fontSize": "1.375em", + "lineHeight": "1.2495", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale em m 1`] = ` +Object { + "fontSize": "1em", + "lineHeight": "1.5000", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale em s 1`] = ` +Object { + "fontSize": "0.875em", + "lineHeight": "1.5000", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale em xl 1`] = ` +Object { + "fontSize": "1.6875em", + "lineHeight": "1.2495", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale em xs 1`] = ` +Object { + "fontSize": "0.75em", + "lineHeight": "1.5000", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale em xxl 1`] = ` +Object { + "fontSize": "2.125em", + "lineHeight": "1.2495", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale em xxs 1`] = ` +Object { + "fontSize": "0.6875em", + "lineHeight": "1.5000", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale em xxxs 1`] = ` +Object { + "fontSize": "0.5625em", + "lineHeight": "1.5000", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px l 1`] = ` +Object { + "fontSize": "22px", + "lineHeight": "24px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px m 1`] = ` +Object { + "fontSize": "16px", + "lineHeight": "24px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px s 1`] = ` +Object { + "fontSize": "14px", + "lineHeight": "20px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px xl 1`] = ` +Object { + "fontSize": "27px", + "lineHeight": "32px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px xs 1`] = ` +Object { + "fontSize": "12px", + "lineHeight": "16px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px xxl 1`] = ` +Object { + "fontSize": "34px", + "lineHeight": "40px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px xxs 1`] = ` +Object { + "fontSize": "11px", + "lineHeight": "16px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale px xxxs 1`] = ` +Object { + "fontSize": "9px", + "lineHeight": "12px", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem l 1`] = ` +Object { + "fontSize": "1.5714rem", + "lineHeight": "1.7143rem", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem m 1`] = ` +Object { + "fontSize": "1.1429rem", + "lineHeight": "1.7143rem", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem s 1`] = ` +Object { + "fontSize": "1.0000rem", + "lineHeight": "1.4286rem", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem xl 1`] = ` +Object { + "fontSize": "1.9286rem", + "lineHeight": "2.2857rem", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem xs 1`] = ` +Object { + "fontSize": "0.8571rem", + "lineHeight": "1.1429rem", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem xxl 1`] = ` +Object { + "fontSize": "2.4286rem", + "lineHeight": "2.8571rem", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem xxs 1`] = ` +Object { + "fontSize": "0.7857rem", + "lineHeight": "1.1429rem", +} +`; + +exports[`euiFontSize returns an object of font-size and line-height for each scale rem xxxs 1`] = ` +Object { + "fontSize": "0.6429rem", + "lineHeight": "0.8571rem", +} +`; + +exports[`euiNumberFormat returns a string of CSS text 1`] = ` +" + font-feature-settings: 'calt' 1, 'kern' 1, 'liga' 1, 'tnum' 1; +" +`; + +exports[`euiTextBreakWord returns a string of CSS text 1`] = ` +" + overflow-wrap: break-word !important; // makes sure the long string will wrap and not bust out of the container + word-wrap: break-word !important; // spec says, they are literally just alternate names for each other but some browsers support one and not the other + word-break: break-word; // IE doesn't understand but that's ok +" +`; + +exports[`euiTextTruncate allows customizing max-width 1`] = ` +" + max-width: 150px; // Ensure that the node has a maximum width after which truncation can occur + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; +" +`; + +exports[`euiTextTruncate returns a string of CSS text 1`] = ` +" + max-width: 100%; // Ensure that the node has a maximum width after which truncation can occur + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; +" +`; diff --git a/src/global_styling/mixins/_typography.test.ts b/src/global_styling/mixins/_typography.test.ts new file mode 100644 index 00000000000..26284d1d95f --- /dev/null +++ b/src/global_styling/mixins/_typography.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { testCustomHook } from '../../test/internal'; + +import { + EuiThemeFontScales, + EuiThemeFontSizeMeasurements, +} from '../variables/typography'; +import { + useEuiFontSize, + euiTextBreakWord, + euiTextTruncate, + useEuiNumberFormat, +} from './_typography'; + +describe('euiFontSize', () => { + describe('returns an object of font-size and line-height for each scale', () => { + EuiThemeFontSizeMeasurements.forEach((measure) => { + describe(measure, () => { + EuiThemeFontScales.forEach((size) => { + test(size, () => { + expect( + testCustomHook(() => useEuiFontSize(size, measure)).return + ).toMatchSnapshot(); + }); + }); + }); + }); + }); +}); + +describe('euiTextBreakWord', () => { + it('returns a string of CSS text', () => { + expect(euiTextBreakWord()).toMatchSnapshot(); + }); +}); + +describe('euiTextTruncate', () => { + it('returns a string of CSS text', () => { + expect(euiTextTruncate()).toMatchSnapshot(); + }); + + it('allows customizing max-width', () => { + expect(euiTextTruncate('150px')).toMatchSnapshot(); + }); +}); + +describe('euiNumberFormat', () => { + it('returns a string of CSS text', () => { + expect(testCustomHook(() => useEuiNumberFormat()).return).toMatchSnapshot(); + }); +}); diff --git a/src/global_styling/mixins/_typography.ts b/src/global_styling/mixins/_typography.ts index 07322cacfd4..119cab9ae1a 100644 --- a/src/global_styling/mixins/_typography.ts +++ b/src/global_styling/mixins/_typography.ts @@ -46,12 +46,35 @@ export const useEuiFontSize = ( }; /** - * Text utilities + * Force text to wrap on natural word breaks (e.g. spaces & hyphens) + * https://css-tricks.com/snippets/css/prevent-long-urls-from-breaking-out-of-container/ */ - -// https://css-tricks.com/snippets/css/prevent-long-urls-from-breaking-out-of-container/ -export const euiTextBreakWord = ` +export const euiTextBreakWord = () => ` overflow-wrap: break-word !important; // makes sure the long string will wrap and not bust out of the container word-wrap: break-word !important; // spec says, they are literally just alternate names for each other but some browsers support one and not the other word-break: break-word; // IE doesn't understand but that's ok `; + +/** + * Prevent text from wrapping onto multiple lines, and truncate with an ellipsis. + */ +export const euiTextTruncate = ( + maxWidth: CSSProperties['maxWidth'] = '100%' +) => ` + max-width: ${maxWidth}; // Ensure that the node has a maximum width after which truncation can occur + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; +`; + +/** + * Fixed-width numbers for tabular data + */ +export const euiNumberFormat = (euiTheme: UseEuiTheme['euiTheme']) => ` + font-feature-settings: ${euiTheme.font.featureSettings}, 'tnum' 1; +`; +// Hook version +export const useEuiNumberFormat = (): string => { + const { euiTheme } = useEuiTheme(); + return euiNumberFormat(euiTheme); +}; diff --git a/upcoming_changelogs/5854.md b/upcoming_changelogs/5854.md new file mode 100644 index 00000000000..6a1efbfbb64 --- /dev/null +++ b/upcoming_changelogs/5854.md @@ -0,0 +1 @@ +- Added `euiTextTruncate`, `euiTextWordBreak`, and `euiNumberFormat` CSS-in-JS text utilities