UNPKG

@dash-ui/layout

Version:

A framework-agnostic layout library written in Dash

766 lines (675 loc) 23.7 kB
import { matchers } from "@dash-ui/jest"; import { createStyles } from "@dash-ui/styles"; import layout from "./index"; // Add the custom matchers provided by '@dash-ui/jest' expect.extend(matchers); describe("box()", () => { it('applies the "display" prop', () => { expect( createElement(layoutStyles.box({ display: "block" })) ).toHaveStyleRule("display", "block"); }); it('applies the "position" prop', () => { expect( createElement(layoutStyles.box({ position: "relative" })) ).toHaveStyleRule("position", "relative"); }); it('applies the "width" prop', () => { expect(createElement(layoutStyles.box({ width: "100%" }))).toHaveStyleRule( "width", "100%" ); }); it('applies the "width" prop w/ px default', () => { expect(createElement(layoutStyles.box({ width: 100 }))).toHaveStyleRule( "width", "100px" ); }); it('applies the "height" prop', () => { expect(createElement(layoutStyles.box({ height: "100%" }))).toHaveStyleRule( "height", "100%" ); }); it('applies the "height" prop w/ px default', () => { expect(createElement(layoutStyles.box({ height: 100 }))).toHaveStyleRule( "height", "100px" ); }); it('applies the "size" prop', () => { const element = createElement(layoutStyles.box({ size: "100%" })); expect(element).toHaveStyleRule("width", "100%"); expect(element).toHaveStyleRule("height", "100%"); }); it('applies the "size" prop w/ px default', () => { const element = createElement(layoutStyles.box({ size: 100 })); expect(element).toHaveStyleRule("width", "100px"); expect(element).toHaveStyleRule("height", "100px"); }); it('applies the "min-width" prop', () => { expect( createElement(layoutStyles.box({ minWidth: "100%" })) ).toHaveStyleRule("min-width", "100%"); }); it('applies the "min-width" prop w/ px default', () => { expect(createElement(layoutStyles.box({ minWidth: 100 }))).toHaveStyleRule( "min-width", "100px" ); }); it('applies the "max-width" prop', () => { expect( createElement(layoutStyles.box({ maxWidth: "100%" })) ).toHaveStyleRule("max-width", "100%"); }); it('applies the "max-width" prop w/ px default', () => { expect(createElement(layoutStyles.box({ maxWidth: 100 }))).toHaveStyleRule( "max-width", "100px" ); }); it('applies the "max-height" prop', () => { expect( createElement(layoutStyles.box({ maxHeight: "100%" })) ).toHaveStyleRule("max-height", "100%"); }); it('applies the "max-height" prop w/ px default', () => { expect(createElement(layoutStyles.box({ maxHeight: 100 }))).toHaveStyleRule( "max-height", "100px" ); }); it('applies the "border" prop w/ px default', () => { const element = createElement( layoutStyles.box({ border: [["hairline", "hairline"], "green"] }) ); expect(element).toHaveStyleRule( "border-width", "var(--border-width-hairline) var(--border-width-hairline)" ); expect(element).toHaveStyleRule("border-style", "solid"); expect(element).toHaveStyleRule("border-color", "var(--color-green)"); }); it('applies the "border" prop w/ array', () => { const element = createElement( layoutStyles.box({ border: ["hairline", "green"] }) ); expect(element).toHaveStyleRule( "border-width", "var(--border-width-hairline)" ); expect(element).toHaveStyleRule("border-style", "solid"); expect(element).toHaveStyleRule("border-color", "var(--color-green)"); }); it('applies the "bg" prop', () => { expect(createElement(layoutStyles.box({ bg: "green" }))).toHaveStyleRule( "background-color", "var(--color-green)" ); }); it('applies the "pad" prop', () => { expect(createElement(layoutStyles.box({ pad: 1 }))).toHaveStyleRule( "padding", "var(--pad-1)" ); }); it('applies the "pad" prop w/ array', () => { expect( createElement(layoutStyles.box({ pad: [0, "auto"] })) ).toHaveStyleRule("padding", "var(--pad-0) var(--pad-auto)"); }); it('applies the "z" prop w/ token', () => { expect(createElement(layoutStyles.box({ z: "min" }))).toHaveStyleRule( "z-index", "var(--z-index-min)" ); }); it('applies the "inset" prop w/ number', () => { const element = createElement(layoutStyles.box({ inset: 1 })); expect(element).toHaveStyleRule("inset", "1px", { supports: "(inset: 10px)", }); expect(element).toHaveStyleRule("top", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("right", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("bottom", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("left", "1px", { supports: "not (inset: 10px)", }); }); it('applies the "inset" prop w/ array', () => { const element = createElement(layoutStyles.box({ inset: [1] })); expect(element).toHaveStyleRule("inset", "1px", { supports: "(inset: 10px)", }); expect(element).toHaveStyleRule("top", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("right", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("bottom", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("left", "1px", { supports: "not (inset: 10px)", }); }); it('applies the "inset" prop w/ array len 2', () => { const element = createElement(layoutStyles.box({ inset: [1, 2] })); expect(element).toHaveStyleRule("inset", "1px 2px", { supports: "(inset: 10px)", }); expect(element).toHaveStyleRule("top", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("right", "2px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("bottom", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("left", "2px", { supports: "not (inset: 10px)", }); }); it('applies the "inset" prop w/ array len 3', () => { const element = createElement(layoutStyles.box({ inset: [1, 2, 3] })); expect(element).toHaveStyleRule("inset", "1px 2px 3px", { supports: "(inset: 10px)", }); expect(element).toHaveStyleRule("top", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("right", "2px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("bottom", "3px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("left", "2px", { supports: "not (inset: 10px)", }); }); it('applies the "inset" prop w/ array len 4', () => { const element = createElement(layoutStyles.box({ inset: [1, 2, 3, 4] })); expect(element).toHaveStyleRule("inset", "1px 2px 3px 4px", { supports: "(inset: 10px)", }); expect(element).toHaveStyleRule("top", "1px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("right", "2px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("bottom", "3px", { supports: "not (inset: 10px)", }); expect(element).toHaveStyleRule("left", "4px", { supports: "not (inset: 10px)", }); }); it('applies the "radius" prop', () => { const element = createElement(layoutStyles.box({ radius: "sm" })); expect(element).toHaveStyleRule("border-radius", "var(--radius-sm)"); }); it('applies the "radius" prop w/ array', () => { const element = createElement(layoutStyles.box({ radius: ["sm", "md"] })); expect(element).toHaveStyleRule( "border-radius", "var(--radius-sm) var(--radius-md)" ); }); it('applies the "shadow" prop', () => { const element = createElement(layoutStyles.box({ shadow: "low" })); expect(element).toHaveStyleRule("box-shadow", "var(--shadow-low)"); }); it("applies responsive styles", () => { const element = createElement( responsiveLayoutStyles.box({ width: { phone: 100, tablet: 200 }, }) ); expect(element).toHaveStyleRule("width", "100px", { media: mediaQueries.phone, }); expect(element).toHaveStyleRule("width", "200px", { media: mediaQueries.tablet, }); }); }); describe("hstack()", () => { it("applies default styles", () => { const element = createElement(layoutStyles.hstack()); expect(element).toHaveStyleRule("display", "flex"); expect(element).toHaveStyleRule("flex-direction", "row"); expect(element).toHaveStyleRule("flex-shrink", "0", { target: ">*", }); }); it('applies the "reversed" prop', () => { const element = createElement(layoutStyles.hstack({ reversed: true })); expect(element).not.toHaveStyleRule("flex-direction", "row"); expect(element).toHaveStyleRule("flex-direction", "row-reverse"); }); it('applies the "gap" prop', () => { const element = createElement(layoutStyles.hstack({ gap: 1 })); expect(element).toHaveStyleRule("gap", "var(--gap-1)", { supports: "(display: flex) and (gap: 1em)", }); expect(element).toHaveStyleRule("margin-left", "var(--gap-1)!important", { target: ">* + *", supports: "not (display: flex) and (gap: 1em)", }); }); it('applies the "gap" prop w/ a negative margin', () => { const element = createElement(layoutStyles.hstack({ gap: "-1" })); expect(element).toHaveStyleRule("margin-left", "var(--gap--1)!important", { target: ">* + *", }); }); it('applies the "align" prop', () => { const element = createElement(layoutStyles.hstack({ align: "center" })); expect(element).toHaveStyleRule("align-items", "center"); }); it('applies the "distribute" prop', () => { const element = createElement( layoutStyles.hstack({ distribute: "center" }) ); expect(element).toHaveStyleRule("justify-content", "center"); }); }); describe("vstack()", () => { it("applies default styles", () => { const element = createElement(layoutStyles.vstack()); expect(element).toHaveStyleRule("display", "flex"); expect(element).toHaveStyleRule("flex-direction", "column"); expect(element).toHaveStyleRule("flex-shrink", "0", { target: ">*", }); }); it('applies the "reversed" prop', () => { const element = createElement(layoutStyles.vstack({ reversed: true })); expect(element).not.toHaveStyleRule("flex-direction", "column"); expect(element).toHaveStyleRule("flex-direction", "column-reverse"); }); it('applies the "gap" prop', () => { const element = createElement(layoutStyles.vstack({ gap: 1 })); expect(element).toHaveStyleRule("gap", "var(--gap-1)", { supports: "(display: flex) and (gap: 1em)", }); expect(element).toHaveStyleRule("margin-top", "var(--gap-1)!important", { target: ">* + *", supports: "not (display: flex) and (gap: 1em)", }); }); it('applies the "gap" prop w/ a negative margin', () => { const element = createElement(layoutStyles.vstack({ gap: "-1" })); expect(element).toHaveStyleRule("margin-top", "var(--gap--1)!important", { target: ">* + *", }); }); it('applies the "align" prop', () => { const element = createElement(layoutStyles.vstack({ align: "center" })); expect(element).toHaveStyleRule("align-items", "center"); }); it('applies the "distribute" prop', () => { const element = createElement( layoutStyles.vstack({ distribute: "center" }) ); expect(element).toHaveStyleRule("justify-content", "center"); }); }); describe("zstack()", () => { it("applies default styles", () => { const element = createElement(layoutStyles.zstack()); expect(element).toHaveStyleRule("display", "grid"); expect(element).toHaveStyleRule("grid-area", "1/1/1/1", { target: ">*", }); }); it('applies the "inline" prop', () => { const element = createElement(layoutStyles.zstack({ inline: true })); expect(element).not.toHaveStyleRule("display", "grid"); expect(element).toHaveStyleRule("display", "inline-grid"); }); it('applies the "alignX" prop', () => { const element = createElement(layoutStyles.zstack({ alignX: "center" })); expect(element).toHaveStyleRule("justify-items", "center"); }); it('applies the "alignY" prop', () => { const element = createElement(layoutStyles.zstack({ alignY: "center" })); expect(element).toHaveStyleRule("align-items", "center"); }); it('applies the "distributeX" prop', () => { const element = createElement( layoutStyles.zstack({ distributeX: "center" }) ); expect(element).toHaveStyleRule("justify-content", "center"); }); it('applies the "distributeY" prop', () => { const element = createElement( layoutStyles.zstack({ distributeY: "center" }) ); expect(element).toHaveStyleRule("align-content", "center"); }); it('applies the "center" prop', () => { const element = createElement(layoutStyles.zstack({ center: true })); expect(element).toHaveStyleRule("align-items", "center"); expect(element).toHaveStyleRule("justify-items", "center"); }); }); describe("inline()", () => { it("applies default styles", () => { const element = createElement(layoutStyles.inline()); expect(element).toHaveStyleRule("display", "flex"); expect(element).toHaveStyleRule("flex-wrap", "wrap"); expect(element).toHaveStyleRule("justify-content", "flex-start"); expect(element).toHaveStyleRule("flex-shrink", "0", { target: ">*", }); }); it('applies the "gap" prop', () => { const element = createElement(layoutStyles.inline({ gap: 1 })); expect(element).toHaveStyleRule("gap", "var(--gap-1)", { supports: "(display: flex) and (gap: 1em)", }); expect(element).toHaveStyleRule( "margin-left", "calc(-1 * var(--gap-1))!important", { supports: "not (display: flex) and (gap: 1em)", } ); expect(element).toHaveStyleRule( "margin-top", "calc(-1 * var(--gap-1))!important", { supports: "not (display: flex) and (gap: 1em)", } ); expect(element).toHaveStyleRule("margin-top", "var(--gap-1)!important", { target: ">*", supports: "not (display: flex) and (gap: 1em)", }); expect(element).toHaveStyleRule("margin-left", "var(--gap-1)!important", { target: ">*", supports: "not (display: flex) and (gap: 1em)", }); }); it('applies the "align" prop', () => { const element = createElement(layoutStyles.inline({ align: "center" })); expect(element).toHaveStyleRule("align-items", "center"); }); it('applies the "distribute" prop', () => { const element = createElement( layoutStyles.inline({ distribute: "center" }) ); expect(element).toHaveStyleRule("justify-content", "center"); }); }); describe("overlay()", () => { it("applies default styles for each position", () => { const placements = [ "center", "topLeft", "top", "topRight", "right", "bottomRight", "bottom", "bottomLeft", "left", ]; for (const placement of placements) { const element = layoutStyles.overlay.css({ placement: placement as any }); expect(element).toMatchSnapshot(placement); } }); it("applies styles with offsets for each position", () => { const placements = [ "topLeft", "top", "topRight", "right", "bottomRight", "bottom", "bottomLeft", "left", ] as const; for (const placement of placements) { const element = layoutStyles.overlay.css({ placement, offset: 10, }); expect(element).toMatchSnapshot(placement); } }); }); describe("grid()", () => { it("applies default styles", () => { const element = createElement(layoutStyles.grid()); expect(element).toHaveStyleRule("display", "grid"); }); it('applies the "inline" prop', () => { const element = createElement(layoutStyles.grid({ inline: true })); expect(element).toHaveStyleRule("display", "inline-grid"); }); it('applies the "cols" prop w/ specific sizes', () => { const element = createElement( layoutStyles.grid({ cols: [100, "auto", "3rem"] }) ); expect(element).toHaveStyleRule("grid-template-columns", "100px auto 3rem"); }); it('applies the "cols" prop w/ fixed number and size', () => { const element = createElement(layoutStyles.grid({ cols: 3 })); expect(element).toHaveStyleRule( "grid-template-columns", "repeat(3,minmax(0,1fr))" ); }); it('applies the "rows" prop w/ specific sizes', () => { const element = createElement( layoutStyles.grid({ rows: [100, "auto", "3rem"] }) ); expect(element).toHaveStyleRule("grid-template-rows", "100px auto 3rem"); }); it('applies the "rows" prop w/ fixed number and size', () => { const element = createElement(layoutStyles.grid({ rows: 3 })); expect(element).toHaveStyleRule( "grid-template-rows", "repeat(3,minmax(0,1fr))" ); }); it('applies the "gap" prop w/ single value', () => { const element = createElement(layoutStyles.grid({ gap: 1 })); expect(element).toHaveStyleRule("grid-gap", "var(--gap-1) var(--gap-1)"); expect(element).toHaveStyleRule("gap", "var(--gap-1) var(--gap-1)"); }); it('applies the "gap" prop w/ array value', () => { const element = createElement(layoutStyles.grid({ gap: [1, 2] })); expect(element).toHaveStyleRule("grid-gap", "var(--gap-1) var(--gap-2)"); expect(element).toHaveStyleRule("gap", "var(--gap-1) var(--gap-2)"); }); it('applies the "alignX" prop', () => { const element = createElement(layoutStyles.grid({ alignX: "center" })); expect(element).toHaveStyleRule("justify-items", "center"); }); it('applies the "alignY" prop', () => { const element = createElement(layoutStyles.grid({ alignY: "center" })); expect(element).toHaveStyleRule("align-items", "center"); }); it('applies the "distributeX" prop', () => { const element = createElement(layoutStyles.grid({ distributeX: "center" })); expect(element).toHaveStyleRule("justify-content", "center"); }); it('applies the "distributeY" prop', () => { const element = createElement(layoutStyles.grid({ distributeY: "center" })); expect(element).toHaveStyleRule("align-content", "center"); }); }); describe("gridItem()", () => { it('applies the "colStart" prop', () => { const element = createElement(layoutStyles.gridItem({ colStart: "1" })); expect(element).toHaveStyleRule("grid-column-start", "1"); }); it('applies the "colEnd" prop', () => { const element = createElement(layoutStyles.gridItem({ colEnd: "1" })); expect(element).toHaveStyleRule("grid-column-end", "1"); }); it('applies the "rowStart" prop', () => { const element = createElement(layoutStyles.gridItem({ rowStart: "1" })); expect(element).toHaveStyleRule("grid-row-start", "1"); }); it('applies the "rowEnd" prop', () => { const element = createElement(layoutStyles.gridItem({ rowEnd: "1" })); expect(element).toHaveStyleRule("grid-row-end", "1"); }); it('applies the "alignX" prop', () => { const element = createElement( layoutStyles.gridItem({ distribute: "center" }) ); expect(element).toHaveStyleRule("justify-self", "center"); }); it('applies the "alignY" prop', () => { const element = createElement(layoutStyles.gridItem({ align: "center" })); expect(element).toHaveStyleRule("align-self", "center"); }); }); describe("autoGrid()", () => { it('applies the "itemWidth" prop w/ number', () => { const element = createElement(layoutStyles.autoGrid({ itemWidth: 200 })); expect(element).toHaveStyleRule( "grid-template-columns", "repeat(auto-fit, minmax(200px, 1fr))" ); }); it('applies the "itemWidth" prop w/ string', () => { const element = createElement(layoutStyles.autoGrid({ itemWidth: "100%" })); expect(element).toHaveStyleRule( "grid-template-columns", "repeat(auto-fit, minmax(100%, 1fr))" ); }); }); describe("flexItem()", () => { it('applies the "align" prop', () => { const element = createElement(layoutStyles.flexItem({ align: "center" })); expect(element).toHaveStyleRule("align-self", "center"); }); it('applies the "distribute" prop', () => { const element = createElement( layoutStyles.flexItem({ distribute: "center" }) ); expect(element).toHaveStyleRule("justify-self", "center"); }); it('applies the "basis" prop', () => { const element = createElement(layoutStyles.flexItem({ basis: 100 })); expect(element).toHaveStyleRule("flex-basis", "100px"); }); it('applies the "grow" prop', () => { const element = createElement(layoutStyles.flexItem({ grow: 100 })); expect(element).toHaveStyleRule("flex-grow", "100"); }); it('applies the "grow" prop w/ boolean', () => { const element = createElement(layoutStyles.flexItem({ grow: true })); expect(element).toHaveStyleRule("flex-grow", "1"); }); it('applies the "shrink" prop', () => { const element = createElement(layoutStyles.flexItem({ shrink: 100 })); expect(element).toHaveStyleRule("flex-shrink", "100"); }); it('applies the "shrink" prop w/ boolean', () => { const element = createElement(layoutStyles.flexItem({ shrink: true })); expect(element).toHaveStyleRule("flex-shrink", "1"); }); it('applies the "order" prop', () => { const element = createElement(layoutStyles.flexItem({ order: 1 })); expect(element).toHaveStyleRule("order", "1"); }); }); describe("bleed()", () => { it('applies the "amount" prop', () => { expect(createElement(layoutStyles.bleed({ amount: 1 }))).toHaveStyleRule( "margin", "calc(-1 * var(--pad-1))!important" ); }); it('applies the "amount" prop w/ array', () => { expect( createElement(layoutStyles.bleed({ amount: [0, 1] })) ).toHaveStyleRule( "margin", "calc(-1 * var(--pad-0)) calc(-1 * var(--pad-1))!important" ); }); }); const styles = createStyles({ tokens: { gap: { auto: "auto", "-5": "-4rem", "-4": "-2rem", "-3": "-1rem", "-2": "-0.5rem", "-1": "-0.25rem", 0: 0, 1: "0.25rem", 2: "0.5rem", 3: "1rem", 4: "2rem", 5: "4rem", }, pad: { auto: "auto", 0: 0, 1: "0.125rem", 2: "0.25rem", 3: "0.5rem", 4: "1rem", 5: "2rem", 6: "4rem", }, color: { blue: "blue", green: "green", }, shadow: { low: "low", high: "high", }, radius: { sm: "0.125rem", md: "0.25rem", }, borderWidth: { hairline: "0.5px", }, zIndex: { min: -1, } as const, }, }); const mediaQueries = { phone: "only screen and (min-width: 0em)", tablet: "only screen and (min-width: 35em)", desktop: "only screen and (min-width: 80em)", } as const; const layoutStyles = layout(styles); const responsiveLayoutStyles = layout(styles, mediaQueries); function createElement(className: string) { const element = document.createElement("div"); element.classList.add(...className.split(" ")); return element; } afterEach(() => { styles.dash.sheet.flush(); styles.dash.inserted.clear(); });