Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ #24 - feat: add paginator component #16

Merged
merged 5 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion src/components/button/button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
--mykn-button-color-background: var(--theme-color-primary-800);
--mykn-button-color-shadow: var(--theme-shade-1000);
--mykn-button-color-text: var(--theme-shade-0);
--mykn-button-height: auto;
--mykn-button-width: auto;
--mykn-button-offset: 0px;
--mykn-button-padding-v: var(--spacing-v-s);
--mykn-button-padding-h: var(--spacing-h-s);

align-items: center;
appearance: none;
margin: 0;
padding: var(--spacing-v-s) var(--spacing-h-s);
padding: var(--mykn-button-padding-v) var(--mykn-button-padding-h);
background-color: var(--mykn-button-color-background);
border: 1px solid var(--mykn-button-color-border);
border-radius: var(--border-radus-m);
Expand All @@ -21,6 +25,7 @@
display: inline-flex;
flex-wrap: wrap;
gap: 0.5em;
height: var(--mykn-button-height);
font-family: Inter, sans-serif;
font-size: var(--typography-font-size-body-s);
justify-content: center;
Expand All @@ -30,6 +35,18 @@
transition: all var(--animation-duration-fast)
var(--animation-timing-function);
transform: translateY(var(--mykn-button-offset));
width: var(--mykn-button-width);

&--square {
--mykn-button-height: calc(
var(--typography-line-height-body-s) + 2 * var(--spacing-v-s)
);
--mykn-button-width: calc(
var(--typography-line-height-body-s) + 2 * var(--spacing-v-s)
);
--mykn-button-padding-v: 0;
--mykn-button-padding-h: 0;
}

&--variant-primary {
&:focus,
Expand All @@ -45,6 +62,25 @@
}
}

&--variant-outline {
--mykn-button-color-background: transparent;
--mykn-button-color-border: var(--theme-shade-700);
--mykn-button-color-shadow: currentColor;
--mykn-button-color-text: var(--typography-color-body);

&:focus,
&:hover {
--mykn-button-color-background: var(--theme-color-primary-200);
--mykn-button-offset: -2px;
}

&[aria-expanded="true"],
&:active {
--mykn-button-color-background: var(--typography-color-background);
--mykn-button-offset: 0px;
}
}

&--variant-transparent {
--mykn-button-color-background: transparent;
--mykn-button-color-shadow: currentColor;
Expand Down
9 changes: 6 additions & 3 deletions src/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import React, { LegacyRef } from "react";
import "./button.scss";

type BaseButtonProps = {
variant?: "primary" | "transparent";
square?: boolean;
variant?: "primary" | "outline" | "transparent";
};

export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
Expand All @@ -20,11 +21,13 @@ export type ButtonLinkProps = React.AnchorHTMLAttributes<HTMLAnchorElement> &
* @constructor
*/
export const Button = React.forwardRef<HTMLAnchorElement, ButtonProps>(
({ variant = "primary", ...props }, ref) => {
({ square = false, variant = "primary", ...props }, ref) => {
return (
<button
ref={ref as LegacyRef<HTMLButtonElement>}
className={clsx("mykn-button", `mykn-button--variant-${variant}`)}
className={clsx("mykn-button", `mykn-button--variant-${variant}`, {
"mykn-button--square": square,
})}
{...props}
>
{props.children}
Expand Down
4 changes: 4 additions & 0 deletions src/components/form/input/input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,8 @@
&[type="file"] {
border: none;
}

&--variant-transparent {
background-color: transparent;
}
}
19 changes: 19 additions & 0 deletions src/components/form/input/input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Formik } from "formik";
import React from "react";

import { Button } from "../../button";
import { Page } from "../../page";
import { FORM_TEST_DECORATOR } from "../.storybook/decorators";
import { Input } from "./input";

Expand Down Expand Up @@ -168,6 +169,24 @@ export const InputWithCustomSize: Story = {
decorators: [FORM_TEST_DECORATOR],
};

export const TransparentInput: Story = {
args: {
name: "input",
placeholder: "e.g. John Doe",
type: "text",
variant: "transparent",
},
argTypes: FORM_TEST_ARG_TYPES,
decorators: [
FORM_TEST_DECORATOR,
(Story) => (
<Page>
<Story />
</Page>
),
],
};

export const UsageWithFormik: Story = {
args: {
name: "input",
Expand Down
11 changes: 8 additions & 3 deletions src/components/form/input/input.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import clsx from "clsx";
import React, { useEffect, useState } from "react";

import { eventFactory } from "../eventFactory";
Expand All @@ -7,8 +8,11 @@ export type InputProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"value"
> & {
/** The variant (style) of the input. */
variant?: "normal" | "transparent";

/** Gets called when the value is changed */
onChange?: (event: Event) => void;
onChange?: React.ChangeEventHandler<HTMLInputElement>;

/** Input value. */
value?: string | number;
Expand All @@ -23,6 +27,7 @@ export type InputProps = Omit<
export const Input: React.FC<InputProps> = ({
type = "text",
value,
variant = "normal",
onChange,
...props
}) => {
Expand Down Expand Up @@ -54,13 +59,13 @@ export const Input: React.FC<InputProps> = ({
const detail = type === "file" ? input.files : event.target.value;
const changeEvent = eventFactory("change", detail, true, false, false);
input.dispatchEvent(changeEvent);
onChange && onChange(changeEvent);
onChange && onChange(event);
};

return (
<input
ref={inputRef}
className="mykn-input"
className={clsx("mykn-input", `mykn-input--variant-${variant}`)}
type={type}
value={valueState}
onChange={_onChange}
Expand Down
8 changes: 8 additions & 0 deletions src/components/form/select/select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
max-width: 100%;
position: relative;

&--size-fit-content {
width: fit-content;
}

&--variant-transparent {
background-color: transparent;
}

.mykn-icon {
transition: transform var(--animation-duration-medium)
var(--animation-timing-function);
Expand Down
16 changes: 15 additions & 1 deletion src/components/form/select/select.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { action } from "@storybook/addon-actions";
import type { Meta, StoryObj } from "@storybook/react";
import type { Meta, StoryFn, StoryObj } from "@storybook/react";
import { expect, fn, userEvent, waitFor, within } from "@storybook/test";
import { Formik } from "formik";
import React from "react";

import { Button } from "../../button";
import { Page } from "../../page";
import { FORM_TEST_DECORATOR } from "../.storybook/decorators";
import { Select } from "./select";

Expand Down Expand Up @@ -75,6 +76,19 @@ export const SelectComponent: Story = {
},
};

export const TransparentSelect = {
...SelectComponent,
args: { ...SelectComponent.args, variant: "transparent" },
decorators: [
...(SelectComponent.decorators as StoryFn[]),
(Story: StoryFn) => (
<Page>
<Story />
</Page>
),
],
};

export const UsageWithFormik: Story = {
args: {
options: [
Expand Down
44 changes: 28 additions & 16 deletions src/components/form/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
autoUpdate,
flip,
offset,
size,
size as sizeMiddleware,
useClick,
useDismiss,
useFloating,
Expand All @@ -22,12 +22,15 @@ import { eventFactory } from "../eventFactory";
import "./select.scss";

export type SelectProps = React.HTMLAttributes<HTMLDivElement> & {
/** Input name. */
name: string;

/** Can be used to generate `SelectOption` components from an array of objects. */
options: Option[];

/** The clear value (accessible) label. */
labelClear?: string;

/** Input name. */
name?: string;

/**
* Gets called when the selected option is changed
*
Expand All @@ -38,16 +41,19 @@ export type SelectProps = React.HTMLAttributes<HTMLDivElement> & {
*/
onChange?: (event: Event) => void;

/** The clear value (accessible) label. */
labelClear?: string;
/** Placeholder text. */
placeholder?: string;

/** Whether a value is required, a required select can't be cleared. */
required?: boolean;

/** Placeholder text. */
placeholder?: string;
/** Can be set to `fit-content` to apply auto sizing based on content width. */
size?: "fit-content";

value?: Option["value"] | null;

/** The variant (style) of the input. */
variant?: "normal" | "transparent";
} & SelectRequiredConditional;

type SelectRequiredConditional =
Expand All @@ -63,9 +69,12 @@ type SelectRequiredConditional =
/**
* A single (select) option, can be passed to `Select as array.
*/
export type Option = {
label: string;
value?: React.OptionHTMLAttributes<HTMLOptionElement>["value"];
export type Option<
L = number | string,
V = React.OptionHTMLAttributes<HTMLOptionElement>["value"],
> = {
label: L;
value?: V;
selected?: React.OptionHTMLAttributes<HTMLOptionElement>["selected"]; // TODO
};

Expand All @@ -84,7 +93,9 @@ export const Select: React.FC<SelectProps> = ({
labelClear = "Clear value",
placeholder = "",
required = false,
size,
value = null,
variant = "normal",
...props
}) => {
const fakeInputRef = React.useRef<HTMLSelectElement>(null);
Expand All @@ -100,7 +111,7 @@ export const Select: React.FC<SelectProps> = ({
middleware: [
offset(6),
flip(),
size({
sizeMiddleware({
padding: 20,
}),
],
Expand Down Expand Up @@ -172,8 +183,9 @@ export const Select: React.FC<SelectProps> = ({
return (
<>
<div
className={clsx("mykn-select", {
"mykn-select__label--selected": selectedIndex,
className={clsx("mykn-select", `mykn-select--variant-${variant}`, {
"mykn-select--selected": selectedIndex,
[`mykn-select--size-${size}`]: size,
})}
tabIndex={0}
ref={refs.setReference}
Expand All @@ -186,7 +198,7 @@ export const Select: React.FC<SelectProps> = ({
<select
ref={fakeInputRef}
name={name}
value={selectedOptionValue}
defaultValue={selectedOptionValue}
hidden={true}
>
{selectedOptionValue && (
Expand Down Expand Up @@ -269,7 +281,7 @@ const BaseSelectDropdown: React.FC<SelectDropdownProps> = ({
setSelectedIndex,
}) => {
const listRef = React.useRef<Array<HTMLElement | null>>([]);
const listContentRef = React.useRef(options.map((o) => o.label));
const listContentRef = React.useRef(options.map((o) => String(o.label)));
const isTypingRef = React.useRef(false);

const click = useClick(context, { event: "mousedown" });
Expand Down
18 changes: 18 additions & 0 deletions src/components/icon/icon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,22 @@
height: 1.2em;
vertical-align: middle;
width: 1.2em;

&--hidden {
visibility: hidden;
}

&--spin {
animation: spin var(--animation-duration-slow)
var(--animation-timing-function) infinite;
}
}

@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Loading