diff --git a/src/theme/breakpoint/media-query-helper.ts b/src/theme/breakpoint/media-query-helper.ts new file mode 100644 index 000000000..81e13adb5 --- /dev/null +++ b/src/theme/breakpoint/media-query-helper.ts @@ -0,0 +1,47 @@ +import { BreakpointValues } from "./theme-helper"; +import { StyledComponentProps } from "../helpers"; + +// Typings for MedaiQuery +type MaxWidthBreakpoints = "xxs" | "xs" | "sm" | "md" | "lg" | "xl"; +type MinWidthBreakpoints = MaxWidthBreakpoints | "xxl"; +type MediaQueryMinMax = "max-width" | "min-width"; +type MediaQuerySpec = Record< + T, + (props: StyledComponentProps) => string +>; + +const createMediaQueryFunction = ( + type: MediaQueryMinMax, + key: T +) => { + const mappedKey = type === "max-width" ? `${key}-max` : `${key}-min`; + const breakpointFunction = + BreakpointValues[mappedKey as keyof typeof BreakpointValues]; + + return (props: StyledComponentProps) => { + const value = breakpointFunction(props); + return `@media screen and (${type}: ${value}px)`; + }; +}; + +const getMediaQuerySpec = ( + type: "max-width" | "min-width" +): MediaQuerySpec => { + // Conditional breakpoints for max and min widths + const breakpoints = ( + type === "max-width" + ? ["xxs", "xs", "sm", "md", "lg", "xl"] + : ["xxs", "xs", "sm", "md", "lg", "xl", "xxl"] + ) as T[]; + + return breakpoints.reduce((accumulator, key) => { + accumulator[key] = createMediaQueryFunction(type, key); + return accumulator; + }, {} as MediaQuerySpec); +}; + +// Export with typing +export const MediaQuery = { + MaxWidth: getMediaQuerySpec("max-width"), + MinWidth: getMediaQuerySpec("min-width"), +}; diff --git a/src/theme/breakpoint/specs/lifesg-breakpoint-set.ts b/src/theme/breakpoint/specs/lifesg-breakpoint-set.ts new file mode 100644 index 000000000..c8fc0e6ad --- /dev/null +++ b/src/theme/breakpoint/specs/lifesg-breakpoint-set.ts @@ -0,0 +1,41 @@ +import { BreakpointSet } from "../types"; + +export const LifeSgBreakpointSet: BreakpointSet = { + "xxs-min": 0, + "xxs-max": 320, + "xs-min": 321, + "xs-max": 375, + "sm-min": 376, + "sm-max": 420, + "md-min": 421, + "md-max": 767, + "lg-min": 768, + "lg-max": 1023, + "xl-min": 1024, + "xl-max": 1440, + "xxl-min": 1441, + + "xxs-column": 8, + "xs-column": 8, + "sm-column": 8, + "md-column": 8, + "lg-column": 12, + "xl-column": 12, + "xxl-column": 12, + + "xxs-gutter": 16, + "xs-gutter": 16, + "sm-gutter": 16, + "md-gutter": 16, + "lg-gutter": 32, + "xl-gutter": 32, + "xxl-gutter": 32, + + "xxs-margin": 24, + "xs-margin": 24, + "sm-margin": 24, + "md-margin": 24, + "lg-margin": 48, + "xl-margin": 48, + "xxl-margin": 48, +}; diff --git a/src/theme/breakpoint/theme-helper.ts b/src/theme/breakpoint/theme-helper.ts new file mode 100644 index 000000000..3eff7f70d --- /dev/null +++ b/src/theme/breakpoint/theme-helper.ts @@ -0,0 +1,74 @@ +import { StyledComponentProps, getCollection, getValue } from "../helpers"; +import { BreakpointScheme, ThemeCollectionSpec } from "../types"; +import { LifeSgBreakpointSet } from "./specs/lifesg-breakpoint-set"; +import { BreakpointCollectionsMap, BreakpointSet } from "./types"; + +const BreakpointSpec: ThemeCollectionSpec< + BreakpointCollectionsMap, + BreakpointScheme +> = { + collections: { + lifesg: LifeSgBreakpointSet, + }, + defaultValue: "lifesg", +}; + +export const getBreakpoint = (key: keyof BreakpointSet) => { + return (props: StyledComponentProps): number => { + const theme = props.theme; + const breakpointSet: BreakpointSet = getCollection( + BreakpointSpec, + theme?.breakpointScheme + ); + + let value: number | undefined; + + if (theme?.overrides?.breakpoint) { + value = getValue(breakpointSet, key, theme.overrides.breakpoint); + } else { + value = breakpointSet[key]; + } + + return value; + }; +}; + +export const BreakpointValues = { + "xxs-min": getBreakpoint("xxs-min"), + "xxs-max": getBreakpoint("xxs-max"), + "xs-min": getBreakpoint("xs-min"), + "xs-max": getBreakpoint("xs-max"), + "sm-min": getBreakpoint("sm-min"), + "sm-max": getBreakpoint("sm-max"), + "md-min": getBreakpoint("md-min"), + "md-max": getBreakpoint("md-max"), + "lg-min": getBreakpoint("lg-min"), + "lg-max": getBreakpoint("lg-max"), + "xl-min": getBreakpoint("xl-min"), + "xl-max": getBreakpoint("xl-max"), + "xxl-min": getBreakpoint("xxl-min"), + + "xxs-column": getBreakpoint("xxs-column"), + "xs-column": getBreakpoint("xs-column"), + "sm-column": getBreakpoint("sm-column"), + "md-column": getBreakpoint("md-column"), + "lg-column": getBreakpoint("lg-column"), + "xl-column": getBreakpoint("xl-column"), + "xxl-column": getBreakpoint("xxl-column"), + + "xxs-gutter": getBreakpoint("xxs-gutter"), + "xs-gutter": getBreakpoint("xs-gutter"), + "sm-gutter": getBreakpoint("sm-gutter"), + "md-gutter": getBreakpoint("md-gutter"), + "lg-gutter": getBreakpoint("lg-gutter"), + "xl-gutter": getBreakpoint("xl-gutter"), + "xxl-gutter": getBreakpoint("xxl-gutter"), + + "xxs-margin": getBreakpoint("xxs-margin"), + "xs-margin": getBreakpoint("xs-margin"), + "sm-margin": getBreakpoint("sm-margin"), + "md-margin": getBreakpoint("md-margin"), + "lg-margin": getBreakpoint("lg-margin"), + "xl-margin": getBreakpoint("xl-margin"), + "xxl-margin": getBreakpoint("xxl-margin"), +}; diff --git a/src/theme/breakpoint/types.ts b/src/theme/breakpoint/types.ts new file mode 100644 index 000000000..b86755962 --- /dev/null +++ b/src/theme/breakpoint/types.ts @@ -0,0 +1,45 @@ +import { StyledComponentProps } from "../helpers"; +import { BreakpointScheme } from "../types"; + +export interface BreakpointSet { + "xxs-min": number; + "xxs-max": number; + "xs-min": number; + "xs-max": number; + "sm-min": number; + "sm-max": number; + "md-min": number; + "md-max": number; + "lg-min": number; + "lg-max": number; + "xl-min": number; + "xl-max": number; + "xxl-min": number; + "xxs-column": number; + "xs-column": number; + "sm-column": number; + "md-column": number; + "lg-column": number; + "xl-column": number; + "xxl-column": number; + "xxs-gutter": number; + "xs-gutter": number; + "sm-gutter": number; + "md-gutter": number; + "lg-gutter": number; + "xl-gutter": number; + "xxl-gutter": number; + "xxs-margin": number; + "xs-margin": number; + "sm-margin": number; + "md-margin": number; + "lg-margin": number; + "xl-margin": number; + "xxl-margin": number; +} + +export type BreakpointCollectionsMap = { + [key in BreakpointScheme]: BreakpointSet; +}; + +export type BreakpointSetOptions = Partial; diff --git a/src/theme/index.ts b/src/theme/index.ts index fbc70ee57..18f232dab 100644 --- a/src/theme/index.ts +++ b/src/theme/index.ts @@ -1,8 +1,11 @@ +import { BreakpointValues } from "./breakpoint/theme-helper"; import { AnimationValues } from "./animation/theme-helper"; import { BorderValues } from "./border/theme-helper"; import { PrimitiveColour } from "./colour-primitive/theme-helper"; import { ColourSemantic } from "./colour-semantic/theme-helper"; import { FontValues } from "./font/theme-helper"; +import { RadiusValues } from "./radius/theme-helper"; +import { SpacingValues } from "./spacing/theme-helper"; import { ThemeSpec } from "./types"; import { TypographyValues } from "./typography/theme-helper"; @@ -19,11 +22,20 @@ export const Animation = AnimationValues; export const Border = BorderValues; +export const Spacing = SpacingValues; + +export const Radius = RadiusValues; + +export const Breakpoint = BreakpointValues; + export const LifeSGTheme: ThemeSpec = { colourScheme: "lifesg", fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; export const BookingSGTheme: ThemeSpec = { @@ -31,6 +43,9 @@ export const BookingSGTheme: ThemeSpec = { fontScheme: "bookingsg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; export const CcubeTheme: ThemeSpec = { @@ -38,6 +53,9 @@ export const CcubeTheme: ThemeSpec = { fontScheme: "ccube", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; export const MyLegacyTheme: ThemeSpec = { @@ -45,6 +63,9 @@ export const MyLegacyTheme: ThemeSpec = { fontScheme: "mylegacy", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; export const RBSTheme: ThemeSpec = { @@ -52,4 +73,7 @@ export const RBSTheme: ThemeSpec = { fontScheme: "rbs", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; diff --git a/src/theme/radius/specs/lifesg-radius-set.ts b/src/theme/radius/specs/lifesg-radius-set.ts new file mode 100644 index 000000000..93ae89f9e --- /dev/null +++ b/src/theme/radius/specs/lifesg-radius-set.ts @@ -0,0 +1,10 @@ +import { RadiusSet } from "../types"; + +export const LifeSgRadiusSet: RadiusSet = { + none: 0, + xs: 2, + sm: 4, + md: 8, + lg: 12, + full: 9999, +}; diff --git a/src/theme/radius/theme-helper.ts b/src/theme/radius/theme-helper.ts new file mode 100644 index 000000000..e67822249 --- /dev/null +++ b/src/theme/radius/theme-helper.ts @@ -0,0 +1,36 @@ +import { StyledComponentProps, getCollection, getValue } from "../helpers"; +import { RadiusScheme, ThemeCollectionSpec } from "../types"; +import { LifeSgRadiusSet } from "./specs/lifesg-radius-set"; +import { RadiusCollectionsMap, RadiusSet } from "./types"; + +const RadiusSpec: ThemeCollectionSpec = { + collections: { + lifesg: LifeSgRadiusSet, + }, + defaultValue: "lifesg", +}; + +export const getRadius = (key: keyof RadiusSet) => { + return (props: StyledComponentProps): string => { + const theme = props.theme; + const radiusSet: RadiusSet = getCollection( + RadiusSpec, + theme.radiusScheme + ); + + if (theme.overrides && theme.overrides.radius) { + return `${getValue(radiusSet, key, theme.overrides.radius)}px`; + } else { + return `${radiusSet[key]}px`; + } + }; +}; + +export const RadiusValues = { + none: getRadius("none"), + xs: getRadius("xs"), + sm: getRadius("sm"), + md: getRadius("md"), + lg: getRadius("lg"), + full: getRadius("full"), +}; diff --git a/src/theme/radius/types.ts b/src/theme/radius/types.ts new file mode 100644 index 000000000..607854239 --- /dev/null +++ b/src/theme/radius/types.ts @@ -0,0 +1,16 @@ +import { RadiusScheme } from "../types"; + +export type RadiusSet = { + none: number; + xs: number; + sm: number; + md: number; + lg: number; + full: number; +}; + +export type RadiusSetOptions = Partial; + +export type RadiusCollectionsMap = { + [key in RadiusScheme]: RadiusSet; +}; diff --git a/src/theme/spacing/specs/lifesg-spacing-set.ts b/src/theme/spacing/specs/lifesg-spacing-set.ts new file mode 100644 index 000000000..7e2d3d3e7 --- /dev/null +++ b/src/theme/spacing/specs/lifesg-spacing-set.ts @@ -0,0 +1,24 @@ +import { SpacingSet } from "../types"; + +export const LifeSgSpacingSet: SpacingSet = { + "spacing-0": 0, + "spacing-4": 4, + "spacing-8": 8, + "spacing-12": 12, + "spacing-16": 16, + "spacing-20": 20, + "spacing-24": 24, + "spacing-32": 32, + "spacing-40": 40, + "spacing-48": 48, + "spacing-64": 64, + "spacing-72": 72, + + "layout-xs": 8, + "layout-sm": 16, + "layout-md": 24, + "layout-lg": 32, + "layout-xl": 48, + "layout-xxl": 64, + "layout-xxxl": 128, +}; diff --git a/src/theme/spacing/theme-helper.ts b/src/theme/spacing/theme-helper.ts new file mode 100644 index 000000000..27c9140de --- /dev/null +++ b/src/theme/spacing/theme-helper.ts @@ -0,0 +1,50 @@ +import { StyledComponentProps, getCollection, getValue } from "../helpers"; +import { SpacingScheme, ThemeCollectionSpec } from "../types"; +import { LifeSgSpacingSet } from "./specs/lifesg-spacing-set"; +import { SpacingCollectionsMap, SpacingSet } from "./types"; + +const SpacingSpec: ThemeCollectionSpec = { + collections: { + lifesg: LifeSgSpacingSet, + }, + defaultValue: "lifesg", +}; + +export const getSpace = (key: keyof SpacingSet) => { + return (props: StyledComponentProps): string => { + const theme = props.theme; + const spacingSet: SpacingSet = getCollection( + SpacingSpec, + theme.spacingScheme + ); + + if (theme.overrides && theme.overrides.spacing) { + return `${getValue(spacingSet, key, theme.overrides.spacing)}px`; + } else { + return `${spacingSet[key]}px`; + } + }; +}; + +export const SpacingValues = { + "spacing-0": getSpace("spacing-0"), + "spacing-4": getSpace("spacing-4"), + "spacing-8": getSpace("spacing-8"), + "spacing-12": getSpace("spacing-12"), + "spacing-16": getSpace("spacing-16"), + "spacing-20": getSpace("spacing-20"), + "spacing-24": getSpace("spacing-24"), + "spacing-32": getSpace("spacing-32"), + "spacing-40": getSpace("spacing-40"), + "spacing-48": getSpace("spacing-48"), + "spacing-64": getSpace("spacing-64"), + "spacing-72": getSpace("spacing-72"), + + "layout-xs": getSpace("layout-xs"), + "layout-sm": getSpace("layout-sm"), + "layout-md": getSpace("layout-md"), + "layout-lg": getSpace("layout-lg"), + "layout-xl": getSpace("layout-xl"), + "layout-xxl": getSpace("layout-xxl"), + "layout-xxxl": getSpace("layout-xxxl"), +}; diff --git a/src/theme/spacing/types.ts b/src/theme/spacing/types.ts new file mode 100644 index 000000000..9c8865e24 --- /dev/null +++ b/src/theme/spacing/types.ts @@ -0,0 +1,30 @@ +import { SpacingScheme } from "../types"; + +export type SpacingSet = { + "spacing-0": number; + "spacing-4": number; + "spacing-8": number; + "spacing-12": number; + "spacing-16": number; + "spacing-20": number; + "spacing-24": number; + "spacing-32": number; + "spacing-40": number; + "spacing-48": number; + "spacing-64": number; + "spacing-72": number; + + "layout-xs": number; + "layout-sm": number; + "layout-md": number; + "layout-lg": number; + "layout-xl": number; + "layout-xxl": number; + "layout-xxxl": number; +}; + +export type SpacingSetOptions = Partial; + +export type SpacingCollectionsMap = { + [key in SpacingScheme]: SpacingSet; +}; diff --git a/src/theme/types.ts b/src/theme/types.ts index 3bdbc546f..72f47793e 100644 --- a/src/theme/types.ts +++ b/src/theme/types.ts @@ -4,6 +4,9 @@ import { TypographySetOptions } from "./typography/types"; import { FontSetOptions } from "./font/types"; import { AnimationSetOptions } from "./animation/types"; import { BorderSetOptions } from "./border/types"; +import { SpacingSetOptions } from "./spacing/types"; +import { RadiusSetOptions } from "./radius/types"; +import { BreakpointSetOptions } from "./breakpoint/types"; export type { PrimitiveColourSet, @@ -22,6 +25,12 @@ export type { AnimationSet, AnimationSetOptions } from "./animation/types"; export type { BorderSet, BorderSetOptions } from "./border/types"; +export type { RadiusSet, RadiusSetOptions } from "./radius/types"; + +export type { SpacingSet, SpacingSetOptions } from "./spacing/types"; + +export type { BreakpointSet, BreakpointSetOptions } from "./breakpoint/types"; + export type ColourScheme = | "lifesg" | "bookingsg" @@ -32,13 +41,19 @@ export type ColourScheme = export type FontScheme = "lifesg" | "bookingsg" | "rbs" | "mylegacy" | "ccube"; export type AnimationScheme = "lifesg"; export type BorderScheme = "lifesg"; +export type SpacingScheme = "lifesg"; +export type RadiusScheme = "lifesg"; +export type BreakpointScheme = "lifesg"; export interface ThemeSpecOptions { primitiveColour?: PrimitiveColourSetOptions | undefined; semanticColour?: SemanticColourSetOptions | undefined; font?: FontSetOptions | undefined; animation?: AnimationSetOptions | undefined; + spacing?: SpacingSetOptions | undefined; border?: BorderSetOptions | undefined; + radius?: RadiusSetOptions | undefined; + breakpoint?: BreakpointSetOptions | undefined; typography?: TypographySetOptions | undefined; } @@ -47,7 +62,12 @@ export interface ThemeSpec { fontScheme: FontScheme; animationScheme: AnimationScheme; borderScheme: BorderScheme; + spacingScheme: SpacingScheme; + radiusScheme: RadiusScheme; + breakpointScheme: BreakpointScheme; overrides?: ThemeSpecOptions | undefined; + + maxColumns?: any; } export interface ThemeCollectionSpec { diff --git a/tests/theme/theme-animation.spec.tsx b/tests/theme/theme-animation.spec.tsx index 05a766394..cdd2ee6d6 100644 --- a/tests/theme/theme-animation.spec.tsx +++ b/tests/theme/theme-animation.spec.tsx @@ -16,6 +16,9 @@ describe("StyledAnimationTest", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; const duration = "500ms"; @@ -39,6 +42,9 @@ describe("StyledAnimationTest", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", overrides: { animation: { "duration-500": "700ms", diff --git a/tests/theme/theme-border.spec.tsx b/tests/theme/theme-border.spec.tsx index 3f0c84667..febfe6e2b 100644 --- a/tests/theme/theme-border.spec.tsx +++ b/tests/theme/theme-border.spec.tsx @@ -16,6 +16,9 @@ describe("Border Theming Test", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; // Constants for border-top (dashed-default) @@ -64,6 +67,9 @@ describe("Border Theming Test", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; const dashThickness = "2px"; @@ -104,6 +110,9 @@ describe("Border Theming Test", () => { fontScheme: "lifesg", borderScheme: "lifesg", animationScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", overrides: { border: { "width-010": 3, diff --git a/tests/theme/theme-breakpoint.spec.tsx b/tests/theme/theme-breakpoint.spec.tsx new file mode 100644 index 000000000..3a4542cc4 --- /dev/null +++ b/tests/theme/theme-breakpoint.spec.tsx @@ -0,0 +1,54 @@ +import { render } from "@testing-library/react"; +import "jest-styled-components"; +import styled, { ThemeProvider } from "styled-components"; +import { Breakpoint } from "../../src"; +import { ThemeSpec } from "../../src/theme/types"; + +const StyledComponentTest = styled.div` + background-color: red; + + @media (min-width: ${Breakpoint["xs-min"]}px) { + background-color: blue; + } + + @media (min-width: ${Breakpoint["xs-max"]}px) { + background-color: green; + } +`; + +describe("Media Width Breakpoints Test", () => { + const mockTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + }; + it("should apply correct styles based on media width", () => { + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveStyleRule("background-color", "red"); + + expect(container.firstChild).toHaveStyleRule( + "background-color", + "blue", + { + media: `(min-width: 321px)`, + } + ); + + expect(container.firstChild).toHaveStyleRule( + "background-color", + "green", + { + media: `(min-width: 375px)`, + } + ); + }); +}); diff --git a/tests/theme/theme-colour.spec.tsx b/tests/theme/theme-colour.spec.tsx index 13adc68a5..1beca5d2c 100644 --- a/tests/theme/theme-colour.spec.tsx +++ b/tests/theme/theme-colour.spec.tsx @@ -16,6 +16,9 @@ describe("Colour Themeing Test", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; const bgColor = "#1A122C"; @@ -40,6 +43,9 @@ describe("Colour Themeing Test", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", overrides: { primitiveColour: { "primary-10": "#fefefe", @@ -69,6 +75,9 @@ describe("Colour Themeing Test", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", overrides: { semanticColour: { "border-primary": "#fefefe", diff --git a/tests/theme/theme-font.spec.tsx b/tests/theme/theme-font.spec.tsx index e73f1a2cb..15bc08cef 100644 --- a/tests/theme/theme-font.spec.tsx +++ b/tests/theme/theme-font.spec.tsx @@ -20,6 +20,9 @@ describe("StyledTypographyTest", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; const fontSize = "3rem"; @@ -48,6 +51,9 @@ describe("StyledTypographyTest", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", overrides: { typography: { "header-xxl-bold": { @@ -86,6 +92,9 @@ describe("StyledTypographyTest", () => { fontScheme: "lifesg", animationScheme: "lifesg", borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", }; const fontSize = "1.125rem"; diff --git a/tests/theme/theme-media.spec.tsx b/tests/theme/theme-media.spec.tsx new file mode 100644 index 000000000..06e3afac5 --- /dev/null +++ b/tests/theme/theme-media.spec.tsx @@ -0,0 +1,58 @@ +import styled, { ThemeProvider } from "styled-components"; +import { MediaQuery } from "../../src/theme/breakpoint/media-query-helper"; +import { ThemeSpec } from "../../src/theme/types"; +import { render } from "@testing-library/react"; +import "jest-styled-components"; + +const StyledComponentTest = styled.div` + background-color: red; + + ${MediaQuery.MinWidth.xs} { + background-color: yellow; + } + + ${MediaQuery.MaxWidth.xs} { + background-color: purple; + } +`; +describe("MediaQuery Helper Function Test", () => { + const mockTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + }; + + const xsMin = "321px"; + const xsMax = "375px"; + const mdMin = "421px"; + const lgMin = "768px"; + + it("should apply correct styles based on media query spec", () => { + const { container } = render( + + + + ); + expect(container.firstChild).toHaveStyleRule("background-color", "red"); + + expect(container.firstChild).toHaveStyleRule( + "background-color", + "yellow", + { + media: `screen and (min-width: ${xsMin})`, + } + ); + + expect(container.firstChild).toHaveStyleRule( + "background-color", + "purple", + { + media: `screen and (max-width: ${xsMax})`, + } + ); + }); +}); diff --git a/tests/theme/theme-radius.spec.tsx b/tests/theme/theme-radius.spec.tsx new file mode 100644 index 000000000..b9b0c0257 --- /dev/null +++ b/tests/theme/theme-radius.spec.tsx @@ -0,0 +1,108 @@ +import { render } from "@testing-library/react"; +import "jest-styled-components"; +import styled, { ThemeProvider } from "styled-components"; +import { ThemeSpec } from "../../src/theme/types"; +import { Radius } from "../../src"; + +const RadiusTestComponent = styled.div` + border-radius: ${Radius.sm}; +`; + +describe("Radius Theming Test", () => { + it("should apply correct border-radius based on the theme", () => { + const mockTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + }; + + const radiusValue = "4px"; + + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveStyleRule( + "border-radius", + radiusValue + ); + }); + + it("should apply correct border-radius when overriding radius token", () => { + const overrideTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + overrides: { + radius: { + sm: 6, + }, + }, + }; + + const radiusValue = "6px"; + + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveStyleRule( + "border-radius", + radiusValue + ); + }); + + it("should apply correct border-radius when overriding multiple radius tokens", () => { + const RadiusTestComponent2 = styled.div` + border-radius: ${Radius.sm}; + line-height: ${Radius.md}; + `; + + const overrideTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + overrides: { + radius: { + sm: 6, + md: 10, + }, + }, + }; + + const radiusValue = "6px"; + const radiusValue2 = "10px"; + + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveStyleRule( + "border-radius", + radiusValue + ); + + expect(container.firstChild).toHaveStyleRule( + "line-height", + radiusValue2 + ); + }); +}); diff --git a/tests/theme/theme-spacing.spec.tsx b/tests/theme/theme-spacing.spec.tsx new file mode 100644 index 000000000..d668c4d2f --- /dev/null +++ b/tests/theme/theme-spacing.spec.tsx @@ -0,0 +1,95 @@ +import { render } from "@testing-library/react"; +import "jest-styled-components"; +import styled, { ThemeProvider } from "styled-components"; +import { ThemeSpec } from "../../src/theme/types"; +import { Spacing } from "../../src"; + +const SpacingTestComponent = styled.div` + margin: ${Spacing["spacing-16"]}; + padding: ${Spacing["layout-lg"]}; +`; + +describe("Spacing Themeing Test", () => { + it("should apply correct spacing based on the theme", () => { + const mockTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + }; + + const marginValue = "16px"; + const paddingValue = "32px"; + + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveStyleRule("margin", marginValue); + expect(container.firstChild).toHaveStyleRule("padding", paddingValue); + }); + + it("should apply correct spacing when overriding spacing token", () => { + const overrideTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + overrides: { + spacing: { + "spacing-16": 2, + }, + }, + }; + + const marginValue = "2px"; + const paddingValue = "32px"; + + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveStyleRule("margin", marginValue); + expect(container.firstChild).toHaveStyleRule("padding", paddingValue); + }); + + it("should apply correct spacing when overriding spacing token", () => { + const overrideTheme: ThemeSpec = { + colourScheme: "lifesg", + fontScheme: "lifesg", + animationScheme: "lifesg", + borderScheme: "lifesg", + spacingScheme: "lifesg", + radiusScheme: "lifesg", + breakpointScheme: "lifesg", + overrides: { + spacing: { + "spacing-16": 3, + "layout-lg": 40, + }, + }, + }; + + const marginValue = "3px"; + const paddingValue = "40px"; + + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveStyleRule("margin", marginValue); + expect(container.firstChild).toHaveStyleRule("padding", paddingValue); + }); +});