Skip to content

Commit

Permalink
Merge pull request #16 from maykinmedia/feature/#24-paginator
Browse files Browse the repository at this point in the history
#24 - feat: add paginator component
  • Loading branch information
svenvandescheur authored Jan 26, 2024
2 parents 2a82cea + dc925f6 commit 5399457
Show file tree
Hide file tree
Showing 17 changed files with 622 additions and 29 deletions.
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

0 comments on commit 5399457

Please sign in to comment.