Skip to content

Commit

Permalink
Merge pull request #155 from oaknational/feat/pupil-672/oak-pupil-yea…
Browse files Browse the repository at this point in the history
…r-browse-button

feat: added  OakPupilJourneyYearButton
  • Loading branch information
benprotheroe authored May 3, 2024
2 parents 73440f1 + 3b9b5f7 commit 48a5a90
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { Meta, StoryObj } from "@storybook/react";

import { InternalShadowRectButton } from "./InternalShadowRectButton";

import { oakIconNames } from "@/components/atoms/OakIcon";
import { OakIcon, oakIconNames } from "@/components/atoms/OakIcon";
import { OakFlex, OakLI, OakUL } from "@/components/atoms";
import { borderArgTypes } from "@/storybook-helpers/borderStyleHelpers";
import { colorArgTypes } from "@/storybook-helpers/colorStyleHelpers";
import { sizeArgTypes } from "@/storybook-helpers/sizeStyleHelpers";
import { flexArgTypes } from "@/storybook-helpers/flexStyleHelpers";

const controlIconNames = [null, [...oakIconNames].sort()].flat();

Expand All @@ -20,6 +21,8 @@ const meta: Meta<typeof InternalShadowRectButton> = {
options: controlIconNames,
control: { type: "select" },
},
iconLayout: flexArgTypes["$flexDirection"],
iconGap: flexArgTypes["$gap"],
isTrailingIcon: {
control: { type: "boolean" },
},
Expand All @@ -42,6 +45,9 @@ const meta: Meta<typeof InternalShadowRectButton> = {
controls: {
include: [
"iconName",
"iconOverride",
"iconLayout",
"iconGap",
"isTrailingIcon",
"isLoading",
"defaultBackground",
Expand Down Expand Up @@ -161,3 +167,58 @@ export const ButtonInList: Story = {
disabledTextColor: "text-disabled",
},
};

export const VeritcalLayout: Story = {
render: (args) => (
<OakFlex $gap="space-between-m">
<InternalShadowRectButton {...args}>Button</InternalShadowRectButton>
</OakFlex>
),
args: {
iconName: "bell",
iconLayout: "column",
defaultBackground: "bg-btn-secondary",
defaultTextColor: "text-primary",
defaultBorderColor: "text-primary",
hoverBackground: "bg-btn-secondary-hover",
hoverTextColor: "text-primary",
hoverBorderColor: "text-primary",
disabledBackground: "bg-btn-secondary-disabled",
disabledBorderColor: "text-disabled",
disabledTextColor: "text-disabled",
},
};

export const CustomIcon: Story = {
render: (args) => {
const customIcon = (
<OakIcon
iconName="books"
$pa={"inner-padding-m"}
$width={"all-spacing-14"}
$height={"all-spacing-14"}
/>
);
return (
<OakFlex $gap="space-between-m">
<InternalShadowRectButton {...args} iconOverride={customIcon}>
Button
</InternalShadowRectButton>
</OakFlex>
);
},
args: {
iconLayout: "column",
iconGap: "space-between-m",
font: "heading-5",
defaultBackground: "bg-btn-secondary",
defaultTextColor: "text-primary",
defaultBorderColor: "text-primary",
hoverBackground: "bg-btn-secondary-hover",
hoverTextColor: "text-primary",
hoverBorderColor: "text-primary",
disabledBackground: "bg-btn-secondary-disabled",
disabledBorderColor: "text-disabled",
disabledTextColor: "text-disabled",
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { parseColor } from "@/styles/helpers/parseColor";
import { OakCombinedColorToken, OakDropShadowToken } from "@/styles";
import { SizeStyleProps, sizeStyle } from "@/styles/utils/sizeStyle";
import { PolymorphicPropsWithoutRef } from "@/components/polymorphic";
import { SpacingStyleProps } from "@/styles/utils/spacingStyle";
import { FlexStyleProps } from "@/styles/utils/flexStyle";
import { TypographyStyleProps } from "@/styles/utils/typographyStyle";

export type InternalShadowRectButtonProps = Omit<
InternalButtonProps,
Expand All @@ -30,7 +33,19 @@ export type InternalShadowRectButtonProps = Omit<
| "$color"
> & {
iconName?: OakIconName;
/**
* we can set a custom icon if we want different sizes and padding
*/
iconOverride?: React.ReactNode;
isTrailingIcon?: boolean;
/**
* we can arrange the icon vertically or horizontally
*/
iconLayout?: FlexStyleProps["$flexDirection"];
/**
* we can adjust the gap between the icon and the text
*/
iconGap?: FlexStyleProps["$gap"];
defaultTextColor: OakCombinedColorToken;
defaultBackground: OakCombinedColorToken;
defaultBorderColor: OakCombinedColorToken;
Expand All @@ -43,6 +58,9 @@ export type InternalShadowRectButtonProps = Omit<
width?: SizeStyleProps["$width"];
maxWidth?: SizeStyleProps["$maxWidth"];
hoverShadow?: OakDropShadowToken | null;
pv?: SpacingStyleProps["$pv"];
ph?: SpacingStyleProps["$ph"];
font?: TypographyStyleProps["$font"];
} & PositionStyleProps;

const StyledInternalButton = styled(InternalButton)<
Expand Down Expand Up @@ -144,10 +162,16 @@ export const InternalShadowRectButton = <C extends ElementType = "button">(
disabledBorderColor,
className,
hoverShadow = "drop-shadow-lemon",
pv = "inner-padding-s",
ph = "inner-padding-m",
iconLayout = "row",
iconGap = "space-between-ssx",
iconOverride,
font = "heading-7",
...rest
} = props;

const icon = (
const icon = iconOverride ?? (
<>
{iconName && (
<OakIcon
Expand Down Expand Up @@ -201,8 +225,8 @@ export const InternalShadowRectButton = <C extends ElementType = "button">(
$background={defaultBackground}
$borderColor={defaultBorderColor}
$color={defaultTextColor}
$pv={"inner-padding-s"}
$ph={"inner-padding-m"}
$pv={pv}
$ph={ph}
$borderRadius={"border-radius-s"}
$position={"relative"}
disabled={disabled || isLoading}
Expand All @@ -220,13 +244,13 @@ export const InternalShadowRectButton = <C extends ElementType = "button">(
{...rest}
>
<OakFlex
$flexDirection={"row"}
$flexDirection={iconLayout}
$alignItems={"center"}
$gap="space-between-ssx"
$gap={iconGap}
$justifyContent="center"
>
{!isTrailingIcon && iconLogic}
<OakSpan $font={"heading-7"}>{children}</OakSpan>
<OakSpan $font={font}>{children}</OakSpan>
{isTrailingIcon && iconLogic}
</OakFlex>
</StyledInternalButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react";
import { StoryObj, Meta } from "@storybook/react";

import { OakPupilJourneyYearButton } from "./OakPupilJourneyYearButton";

const meta: Meta<typeof OakPupilJourneyYearButton> = {
component: OakPupilJourneyYearButton,
tags: ["autodocs"],
argTypes: {
phase: { control: { type: "radio" }, options: ["primary", "secondary"] },
disabled: { control: { type: "boolean" } },
},
parameters: {
controls: {
include: ["phase", "disabled"],
},
},
};

export default meta;

type Story = StoryObj<typeof OakPupilJourneyYearButton>;

export const Default: Story = {
render: (args) => (
<OakPupilJourneyYearButton {...args}>Year 1</OakPupilJourneyYearButton>
),
args: { phase: "primary", disabled: false },
};

export const Disabled: Story = {
render: (args) => (
<OakPupilJourneyYearButton {...args}>Year 1</OakPupilJourneyYearButton>
),
args: { disabled: true },
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";
import "@testing-library/jest-dom";
import { create } from "react-test-renderer";

import { OakPupilJourneyYearButton } from "./OakPupilJourneyYearButton";

import renderWithTheme from "@/test-helpers/renderWithTheme";
import { OakThemeProvider } from "@/components/atoms";
import { oakDefaultTheme } from "@/styles";

describe("OakPupilJourneyYearButton", () => {
it("renders", () => {
const { getByRole } = renderWithTheme(
<OakPupilJourneyYearButton phase="primary">
Year 1
</OakPupilJourneyYearButton>,
);
expect(getByRole("button", { name: "Year 1" })).toBeInTheDocument();
});

it("matches snapshot", () => {
const tree = create(
<OakThemeProvider theme={oakDefaultTheme}>
<OakPupilJourneyYearButton phase="primary">
Year 1
</OakPupilJourneyYearButton>
</OakThemeProvider>,
).toJSON();
expect(tree).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { ElementType } from "react";

import {
InternalShadowRectButton,
InternalShadowRectButtonProps,
} from "@/components/molecules/InternalShadowRectButton";
import { PolymorphicPropsWithoutRef } from "@/components/polymorphic";

export type OakPupilJourneyYearButtonProps = {
phase: "primary" | "secondary";
} & Omit<
InternalShadowRectButtonProps,
| "defaultBorderColor"
| "defaultBackground"
| "defaultTextColor"
| "hoverBackground"
| "hoverBorderColor"
| "hoverTextColor"
| "disabledBackground"
| "disabledBorderColor"
| "disabledTextColor"
| "pv"
| "ph"
| "font"
>;

/**
*
* A specific implementation of InternalRectButton
*
* Changes colour according to the phase prop. Can be used as a link or a button.
* The following callbacks are available for tracking focus events:
*
* ### onClick
* `onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;`
*
* ### onHovered
* `onHovered?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, duration: number) => void;`<br>
* called after a mouseEnter and mouseLeave event has happened
*/

export const OakPupilJourneyYearButton = <C extends ElementType = "button">({
phase,
element,
...rest
}: OakPupilJourneyYearButtonProps & PolymorphicPropsWithoutRef<C>) => {
const defaultBackground =
phase === "primary"
? "bg-decorative4-very-subdued"
: "bg-decorative3-very-subdued";
const hoverBackground =
phase === "primary" ? "bg-decorative4-main" : "bg-decorative3-main";
const borderColor =
phase === "primary"
? "border-decorative4-stronger"
: "border-decorative3-stronger";

return (
<InternalShadowRectButton
element={element ?? "button"}
pv={"inner-padding-xl"}
ph={"inner-padding-xl"}
font={"heading-6"}
defaultBorderColor={borderColor}
defaultBackground={defaultBackground}
defaultTextColor="text-primary"
hoverBackground={hoverBackground}
hoverBorderColor={borderColor}
hoverTextColor="text-primary"
disabledBackground="bg-btn-secondary-disabled"
disabledBorderColor="border-neutral-lighter"
disabledTextColor="text-subdued"
{...rest}
/>
);
};
Loading

0 comments on commit 48a5a90

Please sign in to comment.