From 5a2f83f460e7477a60aae1895de565c159bbfd14 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 13 Dec 2024 18:07:10 +0000 Subject: [PATCH 1/8] Adding the new Text component Signed-off-by: Charles de Dreuille --- packages/canon/docs/components/Text/Text.tsx | 2 +- .../canon/docs/components/Text/styles.css | 2 +- .../canon/docs/components/Title/Title.tsx | 2 +- .../canon/docs/components/Title/styles.css | 2 +- .../src/components/Text/Text.stories.tsx | 33 ++++++++++++++ packages/canon/src/components/Text/Text.tsx | 43 +++++++++++++++++++ packages/canon/src/components/Text/index.ts | 18 ++++++++ packages/canon/src/components/Text/styles.css | 26 +++++++++++ packages/canon/src/components/Text/types.ts | 22 ++++++++++ packages/canon/src/css/components.css | 1 + packages/canon/src/css/core.css | 12 +++--- 11 files changed, 153 insertions(+), 10 deletions(-) create mode 100644 packages/canon/src/components/Text/Text.stories.tsx create mode 100644 packages/canon/src/components/Text/Text.tsx create mode 100644 packages/canon/src/components/Text/index.ts create mode 100644 packages/canon/src/components/Text/styles.css create mode 100644 packages/canon/src/components/Text/types.ts diff --git a/packages/canon/docs/components/Text/Text.tsx b/packages/canon/docs/components/Text/Text.tsx index 9f3dd6ce90..90804b7da7 100644 --- a/packages/canon/docs/components/Text/Text.tsx +++ b/packages/canon/docs/components/Text/Text.tsx @@ -24,7 +24,7 @@ export const Text = ({ style?: React.CSSProperties; }) => { return ( -
+
{children}
); diff --git a/packages/canon/docs/components/Text/styles.css b/packages/canon/docs/components/Text/styles.css index cbb2b88edb..0092625c27 100644 --- a/packages/canon/docs/components/Text/styles.css +++ b/packages/canon/docs/components/Text/styles.css @@ -14,7 +14,7 @@ * limitations under the License. */ -.text { +.sb-text { font-family: 'Geist', sans-serif; font-size: 16px; line-height: 28px; diff --git a/packages/canon/docs/components/Title/Title.tsx b/packages/canon/docs/components/Title/Title.tsx index 056ccfc14d..59fefad8f9 100644 --- a/packages/canon/docs/components/Title/Title.tsx +++ b/packages/canon/docs/components/Title/Title.tsx @@ -31,7 +31,7 @@ export const Title = ({ if (type === 'h3') Component = 'h3'; return React.createElement(Component, { - className: `title ${type}`, + className: `sb-title ${type}`, style, children, }); diff --git a/packages/canon/docs/components/Title/styles.css b/packages/canon/docs/components/Title/styles.css index a8d00e2af5..cfb0e6e379 100644 --- a/packages/canon/docs/components/Title/styles.css +++ b/packages/canon/docs/components/Title/styles.css @@ -14,7 +14,7 @@ * limitations under the License. */ -.title { +.sb-title { font-family: 'Geist', sans-serif; margin: 0; font-weight: 500; diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx new file mode 100644 index 0000000000..5736cd1973 --- /dev/null +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { Text } from './Text'; + +const meta = { + title: 'Components/Text', + component: Text, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: + ' Lorem ipsum dolor sit amet consectetur. Nec arcu vel lacus magna adipiscing nisi mauris tortor viverra. Enim rhoncus quisque consectetur ligula diam ac lacus massa. Id interdum id pellentesque justo ut massa nibh amet. Odio massa in scelerisque tortor massa integer purus amet enim. Eros sit neque nullam facilisis. Purus massa dignissim aliquet purus eu in. Urna consequat ullamcorper arcu amet dictumst. Commodo praesent turpis fringilla tristique congue volutpat in. Nulla in nulla ultrices lacus. In ultrices id tellus ut. Semper duis in in non proin et pulvinar. Sit amet vulputate lorem sit ipsum quis amet pulvinar ultricies. Imperdiet enim molestie habitant eget. Aenean mollis aenean dolor sodales tempus blandit.', + }, +}; diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx new file mode 100644 index 0000000000..6567eb989d --- /dev/null +++ b/packages/canon/src/components/Text/Text.tsx @@ -0,0 +1,43 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { forwardRef } from 'react'; +import { TextProps } from './types'; + +export const Text = forwardRef( + (props, ref) => { + const { + children, + variant = 'body', + weight = 'regular', + ...restProps + } = props; + + return ( +

+ {children} +

+ ); + }, +); + +Text.displayName = 'Text'; diff --git a/packages/canon/src/components/Text/index.ts b/packages/canon/src/components/Text/index.ts new file mode 100644 index 0000000000..ae1f532b2c --- /dev/null +++ b/packages/canon/src/components/Text/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { Text } from './Text'; +export type { TextProps } from './types'; diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css new file mode 100644 index 0000000000..02e2e73392 --- /dev/null +++ b/packages/canon/src/components/Text/styles.css @@ -0,0 +1,26 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.text { + font-family: var(--canon-font-regular); + padding: 0; + margin: 0; + + &.text-body { + font-size: var(--canon-font-size-body); + line-height: 140%; + } +} diff --git a/packages/canon/src/components/Text/types.ts b/packages/canon/src/components/Text/types.ts new file mode 100644 index 0000000000..e63b66e2d9 --- /dev/null +++ b/packages/canon/src/components/Text/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @public */ +export interface TextProps { + children: React.ReactNode; + variant?: 'subtitle' | 'body' | 'caption' | 'label'; + weight?: 'regular' | 'bold'; +} diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 50978a6710..1bd36990fd 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -23,3 +23,4 @@ @import '../components/Icon/styles.css'; @import '../components/Checkbox/styles.css'; @import '../components/Table/styles.css'; +@import '../components/Text/styles.css'; diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index 95e4b6ce5b..80fe6e78f9 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -17,6 +17,12 @@ /* Normalize */ @import './normalize.css'; +*, +html, +body { + font-family: var(--canon-font-regular); +} + /* Light theme tokens */ :root { /* Colors */ @@ -91,12 +97,6 @@ /* Container */ --canon-container-max-width: 1200px; --canon-container-padding: 1rem; - - *, - html, - body { - font-family: var(--canon-font-regular); - } } /* Dark theme tokens */ From f7a16565a3bbc7f638bb4f93a2a826e9b76eed79 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 15 Dec 2024 09:37:00 +0000 Subject: [PATCH 2/8] Add better support for breakpoints Signed-off-by: Charles de Dreuille --- packages/canon/.storybook/preview.tsx | 47 +++++++++++- .../src/components/Text/Text.stories.tsx | 10 +++ packages/canon/src/components/Text/Text.tsx | 13 +++- packages/canon/src/components/Text/types.ts | 11 ++- packages/canon/src/css/components.css | 1 + packages/canon/src/css/core.css | 10 +-- .../src/hooks/useIsomorphicLayoutEffect.ts | 19 +++++ packages/canon/src/hooks/useMediaQuery.ts | 76 +++++++++++++++++++ packages/canon/src/theme/context.tsx | 47 ++++++++++-- packages/canon/src/types.ts | 23 ++++++ .../canon/src/utils/getResponsiveValue.ts | 41 ++++++++++ 11 files changed, 279 insertions(+), 19 deletions(-) create mode 100644 packages/canon/src/hooks/useIsomorphicLayoutEffect.ts create mode 100644 packages/canon/src/hooks/useMediaQuery.ts create mode 100644 packages/canon/src/types.ts create mode 100644 packages/canon/src/utils/getResponsiveValue.ts diff --git a/packages/canon/.storybook/preview.tsx b/packages/canon/.storybook/preview.tsx index e78a9bb923..aa8f3feb0e 100644 --- a/packages/canon/.storybook/preview.tsx +++ b/packages/canon/.storybook/preview.tsx @@ -11,6 +11,7 @@ import '../src/css/components.css'; // Custom themes import './themes/backstage.css'; +import { ThemeProvider } from '../src/theme/context'; const preview: Preview = { parameters: { @@ -28,6 +29,46 @@ const preview: Preview = { method: 'alphabetical', }, }, + viewport: { + viewports: { + small: { + name: 'Small', + styles: { + width: '640px', + height: '100%', + }, + }, + medium: { + name: 'Medium', + styles: { + width: '768px', + height: '100%', + }, + }, + large: { + name: 'Large', + styles: { + width: '1024px', + height: '100%', + }, + }, + xlarge: { + name: 'XLarge', + styles: { + width: '1280px', + height: '100%', + }, + }, + '2xl': { + name: '2XL', + styles: { + width: '1536px', + height: '100%', + }, + }, + }, + defaultViewport: 'small', + }, }, decorators: [ withThemeByDataAttribute({ @@ -48,7 +89,11 @@ const preview: Preview = { 'var(--canon-background)'; }); - return ; + return ( + + + + ); }, ], }; diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx index 5736cd1973..82d6514764 100644 --- a/packages/canon/src/components/Text/Text.stories.tsx +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -31,3 +31,13 @@ export const Default: Story = { ' Lorem ipsum dolor sit amet consectetur. Nec arcu vel lacus magna adipiscing nisi mauris tortor viverra. Enim rhoncus quisque consectetur ligula diam ac lacus massa. Id interdum id pellentesque justo ut massa nibh amet. Odio massa in scelerisque tortor massa integer purus amet enim. Eros sit neque nullam facilisis. Purus massa dignissim aliquet purus eu in. Urna consequat ullamcorper arcu amet dictumst. Commodo praesent turpis fringilla tristique congue volutpat in. Nulla in nulla ultrices lacus. In ultrices id tellus ut. Semper duis in in non proin et pulvinar. Sit amet vulputate lorem sit ipsum quis amet pulvinar ultricies. Imperdiet enim molestie habitant eget. Aenean mollis aenean dolor sodales tempus blandit.', }, }; + +export const Responsive: Story = { + args: { + ...Default.args, + variant: { + xs: 'label', + md: 'body', + }, + }, +}; diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx index 6567eb989d..1c718786fa 100644 --- a/packages/canon/src/components/Text/Text.tsx +++ b/packages/canon/src/components/Text/Text.tsx @@ -16,6 +16,8 @@ import React, { forwardRef } from 'react'; import { TextProps } from './types'; +import { useTheme } from '../../theme/context'; +import { getResponsiveValue } from '../../utils/getResponsiveValue'; export const Text = forwardRef( (props, ref) => { @@ -26,13 +28,18 @@ export const Text = forwardRef( ...restProps } = props; + const { breakpoint } = useTheme(); + + const responsiveVariant = getResponsiveValue(variant, breakpoint); + const responsiveWeight = getResponsiveValue(weight, breakpoint); + return (

{children}

diff --git a/packages/canon/src/components/Text/types.ts b/packages/canon/src/components/Text/types.ts index e63b66e2d9..b054fdf2cf 100644 --- a/packages/canon/src/components/Text/types.ts +++ b/packages/canon/src/components/Text/types.ts @@ -14,9 +14,16 @@ * limitations under the License. */ +import { Breakpoint } from '../../layout/types'; + /** @public */ export interface TextProps { children: React.ReactNode; - variant?: 'subtitle' | 'body' | 'caption' | 'label'; - weight?: 'regular' | 'bold'; + variant?: + | 'subtitle' + | 'body' + | 'caption' + | 'label' + | Partial>; + weight?: 'regular' | 'bold' | Partial>; } diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 1bd36990fd..ed602b7ab7 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -24,3 +24,4 @@ @import '../components/Checkbox/styles.css'; @import '../components/Table/styles.css'; @import '../components/Text/styles.css'; +@import '../components/Heading/styles.css'; diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index 80fe6e78f9..af1c261cc3 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -65,12 +65,12 @@ body { --canon-font-size-caption: 0.75rem; /* 12px */ --canon-font-size-body: 0.875rem; /* 14px */ --canon-font-size-subtitle: 1rem; /* 16px */ - --canon-font-size-title1: 1.25rem; /* 20px */ - --canon-font-size-title2: 1.5rem; /* 24px */ + --canon-font-size-title5: 1.25rem; /* 20px */ + --canon-font-size-title4: 1.5rem; /* 24px */ --canon-font-size-title3: 2rem; /* 32px */ - --canon-font-size-title4: 3rem; /* 48px */ - --canon-font-size-title5: 4rem; /* 64px */ - --canon-font-size-display: 5.75rem; /* 48px */ + --canon-font-size-title2: 3rem; /* 48px */ + --canon-font-size-title1: 4rem; /* 64px */ + --canon-font-size-display: 5.75rem; /* 92px */ /* Spacing */ --canon-spacing-5xs: 0.125rem; /* 2px */ diff --git a/packages/canon/src/hooks/useIsomorphicLayoutEffect.ts b/packages/canon/src/hooks/useIsomorphicLayoutEffect.ts new file mode 100644 index 0000000000..25ea45124e --- /dev/null +++ b/packages/canon/src/hooks/useIsomorphicLayoutEffect.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useEffect, useLayoutEffect } from 'react'; + +export const useIsomorphicLayoutEffect = + typeof window !== 'undefined' ? useLayoutEffect : useEffect; diff --git a/packages/canon/src/hooks/useMediaQuery.ts b/packages/canon/src/hooks/useMediaQuery.ts new file mode 100644 index 0000000000..9e2cc9ef0b --- /dev/null +++ b/packages/canon/src/hooks/useMediaQuery.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useState } from 'react'; + +import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'; + +type UseMediaQueryOptions = { + defaultValue?: boolean; + initializeWithValue?: boolean; +}; + +const IS_SERVER = typeof window === 'undefined'; + +export function useMediaQuery( + query: string, + { + defaultValue = false, + initializeWithValue = true, + }: UseMediaQueryOptions = {}, +): boolean { + const getMatches = (query: string): boolean => { + if (IS_SERVER) { + return defaultValue; + } + return window.matchMedia(query).matches; + }; + + const [matches, setMatches] = useState(() => { + if (initializeWithValue) { + return getMatches(query); + } + return defaultValue; + }); + + // Handles the change event of the media query. + function handleChange() { + setMatches(getMatches(query)); + } + + useIsomorphicLayoutEffect(() => { + const matchMedia = window.matchMedia(query); + + // Triggered at the first client-side load and if query changes + handleChange(); + + // Use deprecated `addListener` and `removeListener` to support Safari < 14 (#135) + if (matchMedia.addListener) { + matchMedia.addListener(handleChange); + } else { + matchMedia.addEventListener('change', handleChange); + } + + return () => { + if (matchMedia.removeListener) { + matchMedia.removeListener(handleChange); + } else { + matchMedia.removeEventListener('change', handleChange); + } + }; + }, [query]); + + return matches; +} diff --git a/packages/canon/src/theme/context.tsx b/packages/canon/src/theme/context.tsx index 77f43927f2..196c8fbb48 100644 --- a/packages/canon/src/theme/context.tsx +++ b/packages/canon/src/theme/context.tsx @@ -17,28 +17,59 @@ import React, { createContext, useContext, ReactNode } from 'react'; import { IconMap, IconNames } from '../components/Icon/types'; import { defaultIcons } from '../components/Icon/icons'; +import { useMediaQuery } from '../hooks/useMediaQuery'; +import { Breakpoints } from '../types'; + +const defaultBreakpoints: Breakpoints = { + xs: '0px', + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', +}; interface ThemeContextProps { icons: IconMap; + breakpoint: keyof Breakpoints; } const ThemeContext = createContext({ icons: defaultIcons, + breakpoint: 'md', }); +interface ThemeProviderProps { + children?: ReactNode; + overrides?: Partial>; + breakpoints?: Partial; +} + /** @public */ -export const ThemeProvider = ({ - children, - overrides, -}: { - children: ReactNode; - overrides: Partial>; -}) => { +export const ThemeProvider = (props: ThemeProviderProps) => { + const { children, overrides, breakpoints = defaultBreakpoints } = props; + // Merge provided overrides with default icons const combinedIcons = { ...defaultIcons, ...overrides }; + const isBreakpointSm = useMediaQuery(`(min-width: ${breakpoints.sm})`); + const isBreakpointMd = useMediaQuery(`(min-width: ${breakpoints.md})`); + const isBreakpointLg = useMediaQuery(`(min-width: ${breakpoints.lg})`); + const isBreakpointXl = useMediaQuery(`(min-width: ${breakpoints.xl})`); + const isBreakpoint2xl = useMediaQuery(`(min-width: ${breakpoints['2xl']})`); + + // Determine the current breakpoint + const breakpoint = (() => { + if (isBreakpoint2xl) return '2xl'; + if (isBreakpointXl) return 'xl'; + if (isBreakpointLg) return 'lg'; + if (isBreakpointMd) return 'md'; + if (isBreakpointSm) return 'sm'; + return 'xs'; + })(); + return ( - + {children} ); diff --git a/packages/canon/src/types.ts b/packages/canon/src/types.ts new file mode 100644 index 0000000000..236d633f84 --- /dev/null +++ b/packages/canon/src/types.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export interface Breakpoints { + xs: string; + sm: string; + md: string; + lg: string; + xl: string; + '2xl': string; +} diff --git a/packages/canon/src/utils/getResponsiveValue.ts b/packages/canon/src/utils/getResponsiveValue.ts new file mode 100644 index 0000000000..858d0c8bcb --- /dev/null +++ b/packages/canon/src/utils/getResponsiveValue.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Breakpoints } from '../types'; + +export const getResponsiveValue = ( + value: any, + breakpoint: keyof Breakpoints, +) => { + if (typeof value === 'object') { + const breakpointsOrder: (keyof Breakpoints)[] = [ + 'xs', + 'sm', + 'md', + 'lg', + 'xl', + '2xl', + ]; + const index = breakpointsOrder.indexOf(breakpoint); + + for (let i = index; i >= 0; i--) { + if (value[breakpointsOrder[i]]) { + return value[breakpointsOrder[i]]; + } + } + return value['xs']; + } + return value; +}; From 942ecc80787c7254466742b8133d53ce05d600f9 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 15 Dec 2024 09:37:19 +0000 Subject: [PATCH 3/8] Add Heading component Signed-off-by: Charles de Dreuille --- .../components/Heading/Heading.stories.tsx | 42 ++++++++++++++ .../canon/src/components/Heading/Heading.tsx | 45 +++++++++++++++ .../canon/src/components/Heading/index.ts | 18 ++++++ .../canon/src/components/Heading/styles.css | 57 +++++++++++++++++++ .../canon/src/components/Heading/types.ts | 35 ++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 packages/canon/src/components/Heading/Heading.stories.tsx create mode 100644 packages/canon/src/components/Heading/Heading.tsx create mode 100644 packages/canon/src/components/Heading/index.ts create mode 100644 packages/canon/src/components/Heading/styles.css create mode 100644 packages/canon/src/components/Heading/types.ts diff --git a/packages/canon/src/components/Heading/Heading.stories.tsx b/packages/canon/src/components/Heading/Heading.stories.tsx new file mode 100644 index 0000000000..d93c7dbb59 --- /dev/null +++ b/packages/canon/src/components/Heading/Heading.stories.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { Heading } from './Heading'; + +const meta = { + title: 'Components/Heading', + component: Heading, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Heading', + }, +}; + +export const Responsive: Story = { + args: { + ...Default.args, + variant: { + // xs: 'title4', + sm: 'display', + }, + }, +}; diff --git a/packages/canon/src/components/Heading/Heading.tsx b/packages/canon/src/components/Heading/Heading.tsx new file mode 100644 index 0000000000..86a2fc39ee --- /dev/null +++ b/packages/canon/src/components/Heading/Heading.tsx @@ -0,0 +1,45 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { forwardRef } from 'react'; +import { HeadingProps } from './types'; +import { useTheme } from '../../theme/context'; +import { getResponsiveValue } from '../../utils/getResponsiveValue'; + +export const Heading = forwardRef( + (props, ref) => { + const { children, variant = 'title1', ...restProps } = props; + const { breakpoint } = useTheme(); + const responsiveVariant = getResponsiveValue(variant, breakpoint); + + console.log(breakpoint); + console.log(responsiveVariant); + + return ( +

+ {children} +

+ ); + }, +); + +Heading.displayName = 'Heading'; diff --git a/packages/canon/src/components/Heading/index.ts b/packages/canon/src/components/Heading/index.ts new file mode 100644 index 0000000000..69b7c3c1a0 --- /dev/null +++ b/packages/canon/src/components/Heading/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { Heading } from './Heading'; +export type { HeadingProps } from './types'; diff --git a/packages/canon/src/components/Heading/styles.css b/packages/canon/src/components/Heading/styles.css new file mode 100644 index 0000000000..e6e26bb06e --- /dev/null +++ b/packages/canon/src/components/Heading/styles.css @@ -0,0 +1,57 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.text { + font-family: var(--canon-font-regular); + padding: 0; + margin: 0; + + &.text-display { + font-size: var(--canon-font-size-display); + line-height: 100%; + font-weight: var(--canon-font-bold); + } + + &.text-title1 { + font-size: var(--canon-font-size-title1); + line-height: 100%; + font-weight: var(--canon-font-bold); + } + + &.text-title2 { + font-size: var(--canon-font-size-title2); + line-height: 100%; + font-weight: var(--canon-font-bold); + } + + &.text-title3 { + font-size: var(--canon-font-size-title3); + line-height: 100%; + font-weight: var(--canon-font-bold); + } + + &.text-title4 { + font-size: var(--canon-font-size-title4); + line-height: 100%; + font-weight: var(--canon-font-bold); + } + + &.text-title5 { + font-size: var(--canon-font-size-title5); + line-height: 100%; + font-weight: var(--canon-font-bold); + } +} diff --git a/packages/canon/src/components/Heading/types.ts b/packages/canon/src/components/Heading/types.ts new file mode 100644 index 0000000000..86145e5738 --- /dev/null +++ b/packages/canon/src/components/Heading/types.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Breakpoint } from '../../layout/types'; + +/** @public */ +export interface HeadingProps { + children: React.ReactNode; + variant?: + | 'display' + | 'title1' + | 'title2' + | 'title3' + | 'title4' + | 'title5' + | Partial< + Record< + Breakpoint, + 'display' | 'title1' | 'title2' | 'title3' | 'title4' | 'title5' + > + >; +} From 74bcdabdafacb02e9b5438782170bad405f37d8a Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 15 Dec 2024 10:18:01 +0000 Subject: [PATCH 4/8] Create chromatic.config.json Signed-off-by: Charles de Dreuille --- packages/canon/chromatic.config.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 packages/canon/chromatic.config.json diff --git a/packages/canon/chromatic.config.json b/packages/canon/chromatic.config.json new file mode 100644 index 0000000000..7b01a28f2c --- /dev/null +++ b/packages/canon/chromatic.config.json @@ -0,0 +1,6 @@ +{ + "onlyChanged": true, + "projectId": "Project:67584b7e8c2eb09c0422c27e", + "storybookBaseDir": "packages/canon", + "zip": true +} From 43ebf22ce36257a5223ce2dddbb92b038b16d060 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 15 Dec 2024 10:32:40 +0000 Subject: [PATCH 5/8] Update core.css Signed-off-by: Charles de Dreuille --- packages/canon/src/css/core.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index af1c261cc3..e053b7dfb7 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -51,10 +51,10 @@ body { --canon-box-shadow-large: 0 0 0 1px rgba(0, 0, 0, 0.2); /* Font families */ - --canon-font-regular: 'Geist', serif; + --canon-font-regular: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol', 'Noto Color Emoji'; --canon-font-monospace: 'Monospace', monospace; - --canon-font-emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', - 'Noto Color Emoji'; /* Font weights */ --canon-font-normal: 400; From d9a495ee29db8bd363add089116a26bd3ba61c1b Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 15 Dec 2024 12:51:01 +0000 Subject: [PATCH 6/8] Delete chromatic.config.json Signed-off-by: Charles de Dreuille --- packages/canon/chromatic.config.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 packages/canon/chromatic.config.json diff --git a/packages/canon/chromatic.config.json b/packages/canon/chromatic.config.json deleted file mode 100644 index 7b01a28f2c..0000000000 --- a/packages/canon/chromatic.config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "onlyChanged": true, - "projectId": "Project:67584b7e8c2eb09c0422c27e", - "storybookBaseDir": "packages/canon", - "zip": true -} From 03f7cd93f813ba9bc90e39793667b613d65e15d8 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 15 Dec 2024 22:22:17 +0000 Subject: [PATCH 7/8] Fix text color Signed-off-by: Charles de Dreuille --- packages/canon/src/components/Heading/styles.css | 1 + packages/canon/src/components/Text/styles.css | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/canon/src/components/Heading/styles.css b/packages/canon/src/components/Heading/styles.css index e6e26bb06e..d2d6a27576 100644 --- a/packages/canon/src/components/Heading/styles.css +++ b/packages/canon/src/components/Heading/styles.css @@ -16,6 +16,7 @@ .text { font-family: var(--canon-font-regular); + color: var(--canon-text-primary); padding: 0; margin: 0; diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css index 02e2e73392..9e78481c97 100644 --- a/packages/canon/src/components/Text/styles.css +++ b/packages/canon/src/components/Text/styles.css @@ -16,6 +16,7 @@ .text { font-family: var(--canon-font-regular); + color: var(--canon-text-primary); padding: 0; margin: 0; From f978fd1c02d4db03ee744b25def1ebd1f4673455 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 16 Dec 2024 10:54:06 +0000 Subject: [PATCH 8/8] Add as property to improve Signed-off-by: Charles de Dreuille --- .../src/components/Heading/Heading.stories.tsx | 8 ++++++++ .../canon/src/components/Heading/Heading.tsx | 16 ++++++++++------ packages/canon/src/components/Heading/types.ts | 1 + 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/canon/src/components/Heading/Heading.stories.tsx b/packages/canon/src/components/Heading/Heading.stories.tsx index d93c7dbb59..38fd839cf5 100644 --- a/packages/canon/src/components/Heading/Heading.stories.tsx +++ b/packages/canon/src/components/Heading/Heading.stories.tsx @@ -40,3 +40,11 @@ export const Responsive: Story = { }, }, }; + +export const CustomTag: Story = { + args: { + ...Default.args, + variant: 'title5', + as: 'h2', + }, +}; diff --git a/packages/canon/src/components/Heading/Heading.tsx b/packages/canon/src/components/Heading/Heading.tsx index 86a2fc39ee..6e45433b91 100644 --- a/packages/canon/src/components/Heading/Heading.tsx +++ b/packages/canon/src/components/Heading/Heading.tsx @@ -19,17 +19,21 @@ import { HeadingProps } from './types'; import { useTheme } from '../../theme/context'; import { getResponsiveValue } from '../../utils/getResponsiveValue'; -export const Heading = forwardRef( +export const Heading = forwardRef( (props, ref) => { - const { children, variant = 'title1', ...restProps } = props; + const { children, variant = 'title1', as = 'h1', ...restProps } = props; const { breakpoint } = useTheme(); const responsiveVariant = getResponsiveValue(variant, breakpoint); - console.log(breakpoint); - console.log(responsiveVariant); + let Component = as; + if (variant === 'title2') Component = 'h2'; + if (variant === 'title3') Component = 'h3'; + if (variant === 'title4') Component = 'h4'; + if (variant === 'title5') Component = 'h5'; + if (as) Component = as; return ( -

( }`} > {children} -

+ ); }, ); diff --git a/packages/canon/src/components/Heading/types.ts b/packages/canon/src/components/Heading/types.ts index 86145e5738..fb37cf6414 100644 --- a/packages/canon/src/components/Heading/types.ts +++ b/packages/canon/src/components/Heading/types.ts @@ -32,4 +32,5 @@ export interface HeadingProps { 'display' | 'title1' | 'title2' | 'title3' | 'title4' | 'title5' > >; + as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; }