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

Change default variant to primary #401

Closed
wants to merge 2 commits into from
Closed
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
5 changes: 5 additions & 0 deletions .changeset/sixty-years-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-ag/meteor-component-library": major
---

Default variant of button is now primary
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { render, screen } from "@testing-library/vue";
import MtButton from "./mt-button.vue";
import { vi } from "vitest";
import { userEvent } from "@storybook/test";

describe("mt-button", () => {
it("performs an action when clicking on a button", async () => {
// ARRANGE
const handler = vi.fn();

render(MtButton, {
props: {
// @ts-expect-error -- Event handler is not typed because they are inherited
onClick: handler,
},
});

// ACT
await userEvent.click(screen.getByRole("button"));

// ASSERT
expect(handler).toHaveBeenCalledOnce();
});

it.each(["{Enter}", " "])('performs an action when pressing "%s" on a button', async (key) => {
// ARRANGE
const handler = vi.fn();

render(MtButton, {
props: {
// @ts-expect-error -- Event handler is not typed because they are inherited
onClick: handler,
},
});

await userEvent.tab();

// ACT
await userEvent.keyboard(key);

// ASSERT
expect(handler).toHaveBeenCalledOnce();
});

it("does not perform an action when clicking on a disabled button", async () => {
// ARRANGE
const handler = vi.fn();

render(MtButton, {
props: {
// @ts-expect-error -- Event handler is not typed because they are inherited
onClick: handler,
disabled: true,
},
});

// ACT
await userEvent.click(screen.getByRole("button"));

// ASSERT
expect(handler).not.toHaveBeenCalled();
});

it.each(["{Enter}", " "])(
'does not perform an action when pressing "%s" on a disabled button',
async (key) => {
// ARRANGE
const handler = vi.fn();

render(MtButton, {
props: {
// @ts-expect-error -- Event handler is not typed because they are inherited
onClick: handler,
disabled: true,
},
});

await userEvent.tab();

// ACT
await userEvent.keyboard(key);

// ASSERT
expect(handler).not.toHaveBeenCalled();
},
);
});
163 changes: 45 additions & 118 deletions packages/component-library/src/components/form/mt-button/mt-button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,133 +21,60 @@
v-bind="$attrs"
>
<mt-loader v-if="isLoading" size="16px" class="mt-button__loader" />
<span class="mt-button__content" :class="contentVisibilityClass">
<span
class="mt-button__content"
:class="{
'mt-button__content--hidden': isLoading,
}"
>
<slot name="iconFront" :size="iconSize" v-if="$slots.iconFront" />
<slot></slot>
<slot />
<slot name="iconBack" :size="iconSize" v-if="$slots.iconBack" />
</span>
</button>
</template>

<script lang="ts">
import { defineComponent } from "vue";
<script setup lang="ts">
import MtLoader from "../../feedback-indicator/mt-loader/mt-loader.vue";
import type { PropType } from "vue";

export default defineComponent({
name: "MtButton",

components: {
"mt-loader": MtLoader,
},

props: {
/**
* Disables the button
*/
disabled: {
type: Boolean,
required: false,
default: false,
},
/**
* Change the look of the button
* Values: primary, secondary, critical, action
* @values primary, secondary, critical, action
*/
variant: {
type: String as PropType<"primary" | "secondary" | "critical" | "action">,
required: false,
default: "",
validator(value: string) {
if (!value.length) {
return true;
}
return ["primary", "secondary", "critical", "action"].includes(value);
},
},
ghost: {
type: Boolean,
required: false,
default: false,
},
/**
* Change the size of the button
* @values x-small, small, default, large
*/
size: {
type: String,
required: false,
default: "small",
validator(value: string) {
if (!value.length) {
return true;
}
return ["x-small", "small", "default", "large"].includes(value);
},
},
/**
* The button will be rendered as a square. You need to consider the text length
* if you activate this property.
*/
square: {
type: Boolean,
required: false,
default: false,
},
/**
* Renders the button as a block element instead of an inline-block element. The button
* fills up all horizontal space.
*/
block: {
type: Boolean,
required: false,
default: false,
},
/**
* If a link is provided then the user gets redirected to a new tab on a click.
*/
link: {
type: String,
required: false,
default: null,
},
/**
* Shows a loading indicator instead of the text.
*/
isLoading: {
type: Boolean,
default: false,
required: false,
},
},

computed: {
buttonClasses() {
return {
[`mt-button--${this.variant}${this.allowGhostVariant ? "-ghost" : ""}`]: !!this.variant,
[`mt-button--${this.size}`]: !!this.size,
"mt-button--block": this.block,
"mt-button--disabled": this.disabled,
"mt-button--square": this.square,
};
},

allowGhostVariant() {
return this.ghost && this.variant !== "secondary";
},

contentVisibilityClass() {
return {
"mt-button__content--hidden": this.isLoading,
};
},

iconSize() {
return this.size === "x-small" ? 8 : this.size === "large" ? 12 : 10;
},
import { computed } from "vue";

const props = withDefaults(
defineProps<{
disabled?: boolean;
variant?: "primary" | "secondary" | "critical" | "action";
ghost?: boolean;
size?: "x-small" | "small" | "default" | "large";
square?: boolean;
block?: boolean;
link?: string;
isLoading?: boolean;
}>(),
{
variant: "primary",
// TODO: Update default value to "default"
default: "small",
},
);

defineSlots<{
default: null;
iconFront: { size: number };
iconBack: { size: number };
}>();

const allowGhostVariant = computed(() => props.ghost && props.variant !== "secondary");

const buttonClasses = computed(() => {
return {
[`mt-button--${props.variant}${allowGhostVariant.value ? "-ghost" : ""}`]: !!props.variant,
[`mt-button--${props.size}`]: !!props.size,
"mt-button--block": props.block,
"mt-button--disabled": props.disabled,
"mt-button--square": props.square,
};
});

const iconSize = computed(() => (props.size === "x-small" ? 8 : props.size === "large" ? 12 : 10));
</script>

<style lang="css" scoped>
Expand Down
Loading