Compare commits

..
9 Commits
Author SHA1 Message Date
Alex van Andel 1896efa3a0 v3.2.7.1
release.yml / release (push) Canceled after 0s
2023-09-01 17:45:59 +01:00
Alex van Andel 62c8603863 fix: Revert #10366: Undoing then redo fix differently (#11078)
* Revert #10366: Undoing then redo fix differently

* Re-implement defaulting to first day in the month

* Update Embed.tsx and DateOverrideInputDialog
2023-09-01 17:12:01 +01:00
7d8559b0b0 chore: add Dropdown in storybook (#10874)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-09-01 18:08:34 +02:00
sean-brydon 7a48bbe553 Remove background colour (#11082) 2023-09-01 15:55:38 +00:00
nidhi sharma 817e95c246 fix #11077 space added on team events (#11081) 2023-09-01 15:30:37 +00:00
Udit Takkar 4ac6225a35 fix: unauthorized error in organization (#11039)
* fix: unauthorized error in organization

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: allow instance admin to update

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-09-01 14:02:58 +00:00
sean-brydon 38d9b93dee Fix avatar (#11076) 2023-09-01 14:23:28 +01:00
sean-brydonandUdit Takkar 376b38d4ed fix: Fixes username invite issue (#10998)
* Fixes username invite issue

* Ensure we only suggest email invites in org members

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-09-01 17:31:45 +05:30
Hariom Balhara 0e181f6d9f Revert "feat: adds next cold start profiler (#11014)" (#11072)
This reverts commit 05631d093f.
2023-09-01 10:18:17 +01:00
17 changed files with 475 additions and 101 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "3.2.7",
"version": "3.2.7.1",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
+1 -1
View File
@@ -873,7 +873,7 @@ const Main = ({
<MobileTeamsTab eventTypeGroups={data.eventTypeGroups} />
) : (
data.eventTypeGroups.map((group: EventTypeGroup, index: number) => (
<div className="flex flex-col" key={group.profile.slug}>
<div className="mt-4 flex flex-col" key={group.profile.slug}>
<EventTypeListHeading
profile={group.profile}
membershipCount={group.metadata.membershipCount}
-8
View File
@@ -1,8 +0,0 @@
{
"builds": [
{
"src": "package.json",
"use": "https://profile-next-cold-starts.vercel.app/builder.tgz"
}
]
}
@@ -387,7 +387,7 @@ export const AUTH_OPTIONS: AuthOptions = {
if (trigger === "update") {
return {
...token,
locale: session?.locale ?? token.locale,
locale: session?.locale ?? token.locale ?? "en",
name: session?.name ?? token.name,
username: session?.username ?? token.username,
email: session?.email ?? token.email,
@@ -1,5 +1,6 @@
import { shallow } from "zustand/shallow";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { default as DatePickerComponent } from "@calcom/features/calendars/DatePicker";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
@@ -23,8 +24,13 @@ export const DatePicker = () => {
return (
<DatePickerComponent
isLoading={schedule.isLoading}
onChange={(date) => setSelectedDate(date ? date.format("YYYY-MM-DD") : date)}
onMonthChange={(date) => setMonth(date.format("YYYY-MM"))}
onChange={(date: Dayjs | null) => {
setSelectedDate(date === null ? date : date.format("YYYY-MM-DD"));
}}
onMonthChange={(date: Dayjs) => {
setMonth(date.format("YYYY-MM"));
setSelectedDate(date.format("YYYY-MM-DD"));
}}
includedDates={nonEmptyScheduleDays}
locale={i18n.language}
browsingDate={month ? dayjs(month) : undefined}
+8 -2
View File
@@ -154,14 +154,19 @@ export const useBookerStore = create<BookerStore>((set, get) => ({
},
selectedDate: getQueryParam("date") || null,
setSelectedDate: (selectedDate: string | null) => {
// unset selected date
if (!selectedDate) {
removeQueryParam("date");
return;
}
const currentSelection = dayjs(get().selectedDate);
const newSelection = dayjs(selectedDate);
set({ selectedDate });
updateQueryParam("date", selectedDate ?? "");
// Setting month make sure small calendar in fullscreen layouts also updates.
// If selectedDate is null, prevents setting month to Invalid-Date
if (selectedDate && newSelection.month() !== currentSelection.month()) {
if (newSelection.month() !== currentSelection.month()) {
set({ month: newSelection.format("YYYY-MM") });
updateQueryParam("month", newSelection.format("YYYY-MM"));
}
@@ -194,6 +199,7 @@ export const useBookerStore = create<BookerStore>((set, get) => ({
setMonth: (month: string | null) => {
set({ month, selectedTimeslot: null });
updateQueryParam("month", month ?? "");
get().setSelectedDate(null);
},
isTeamEvent: false,
seatedEventData: {
+51 -54
View File
@@ -30,7 +30,7 @@ export type DatePickerProps = {
/** Defaults to [], which dates are not bookable. Array of valid dates like: ["2022-04-23", "2022-04-24"] */
excludedDates?: string[];
/** defaults to all, which dates are bookable (inverse of excludedDates) */
includedDates?: string[] | null;
includedDates?: string[];
/** allows adding classes to the container */
className?: string;
/** Shows a small loading spinner next to the month name */
@@ -100,6 +100,40 @@ const NoAvailabilityOverlay = ({
);
};
/**
* Takes care of selecting a valid date in the month if the selected date is not available in the month
*/
const useHandleInitialDateSelection = ({
daysToRenderForTheMonth,
selected,
onChange,
}: {
daysToRenderForTheMonth: { day: Dayjs | null; disabled: boolean }[];
selected: Dayjs | Dayjs[] | null | undefined;
onChange: (date: Dayjs | null) => void;
}) => {
// Let's not do something for now in case of multiple selected dates as behaviour is unclear and it's not needed at the moment
if (selected instanceof Array) {
return;
}
const firstAvailableDateOfTheMonth = daysToRenderForTheMonth.find((day) => !day.disabled)?.day;
const isSelectedDateAvailable = selected
? daysToRenderForTheMonth.some(({ day, disabled }) => {
if (day && yyyymmdd(day) === yyyymmdd(selected) && !disabled) return true;
})
: false;
if (!isSelectedDateAvailable && firstAvailableDateOfTheMonth) {
// If selected date not available in the month, select the first available date of the month
onChange(firstAvailableDateOfTheMonth);
}
if (!firstAvailableDateOfTheMonth) {
onChange(null);
}
};
const Days = ({
minDate = dayjs.utc(),
excludedDates = [],
@@ -121,7 +155,7 @@ const Days = ({
// Create placeholder elements for empty days in first week
const weekdayOfFirst = browsingDate.date(1).day();
const currentDate = minDate.utcOffset(browsingDate.utcOffset());
const availableDates = (includedDates: string[] | undefined | null) => {
const availableDates = (includedDates: string[] | undefined) => {
const dates = [];
const lastDateOfMonth = browsingDate.date(daysInMonth(browsingDate));
for (
@@ -148,21 +182,6 @@ const Days = ({
days.push(date);
}
const daysToRenderForTheMonth = days.map((day) => {
if (!day) return { day: null, disabled: true };
return {
day: day,
disabled:
(includedDates && !includedDates.includes(yyyymmdd(day))) || excludedDates.includes(yyyymmdd(day)),
};
});
useHandleInitialDateSelection({
daysToRenderForTheMonth,
selected,
onChange: props.onChange,
});
const [selectedDatesAndTimes] = useBookerStore((state) => [state.selectedDatesAndTimes], shallow);
const isActive = (day: dayjs.Dayjs) => {
@@ -190,6 +209,21 @@ const Days = ({
return false;
};
const daysToRenderForTheMonth = days.map((day) => {
if (!day) return { day: null, disabled: true };
return {
day: day,
disabled:
(includedDates && !includedDates.includes(yyyymmdd(day))) || excludedDates.includes(yyyymmdd(day)),
};
});
useHandleInitialDateSelection({
daysToRenderForTheMonth,
selected,
onChange: props.onChange,
});
return (
<>
{daysToRenderForTheMonth.map(({ day, disabled }, idx) => (
@@ -305,41 +339,4 @@ const DatePicker = ({
);
};
/**
* Takes care of selecting a valid date in the month if the selected date is not available in the month
*/
const useHandleInitialDateSelection = ({
daysToRenderForTheMonth,
selected,
onChange,
}: {
daysToRenderForTheMonth: { day: Dayjs | null; disabled: boolean }[];
selected: Dayjs | Dayjs[] | null | undefined;
onChange: (date: Dayjs | null) => void;
}) => {
// Let's not do something for now in case of multiple selected dates as behaviour is unclear and it's not needed at the moment
if (selected instanceof Array) {
return;
}
const firstAvailableDateOfTheMonth = daysToRenderForTheMonth.find((day) => !day.disabled)?.day;
const isSelectedDateAvailable = selected
? daysToRenderForTheMonth.some(({ day, disabled }) => {
if (day && yyyymmdd(day) === yyyymmdd(selected) && !disabled) return true;
})
: false;
if (firstAvailableDateOfTheMonth) {
// If selected date not available in the month, select the first available date of the month
if (!isSelectedDateAvailable) {
onChange(firstAvailableDateOfTheMonth);
}
} else {
// No date is available and if we were asked to select something inform that it couldn't be selected. This would actually help in not showing the timeslots section(with No Time Available) when no date in the month is available
if (selected) {
onChange(null);
}
}
};
export default DatePicker;
@@ -32,6 +32,7 @@ import { GoogleWorkspaceInviteButton } from "./GoogleWorkspaceInviteButton";
type MemberInvitationModalProps = {
isOpen: boolean;
justEmailInvites?: boolean;
onExit: () => void;
orgMembers?: RouterOutputs["viewer"]["organizations"]["getMembers"];
onSubmit: (values: NewMemberForm, resetFields: () => void) => void;
@@ -206,7 +207,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
render={({ field: { onChange }, fieldState: { error } }) => (
<>
<TextField
label={t("email_or_username")}
label={props.justEmailInvites ? t("email") : t("email_or_username")}
id="inviteUser"
name="inviteUser"
placeholder="email@example.com"
+22 -15
View File
@@ -8,6 +8,7 @@ import type { ControlProps } from "react-select";
import { components } from "react-select";
import { shallow } from "zustand/shallow";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { AvailableTimes } from "@calcom/features/bookings";
import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
@@ -223,21 +224,27 @@ const EmailEmbed = ({ eventType, username }: { eventType?: EventType; username:
return (
<div className="flex flex-col">
<div className="mb-[9px] font-medium">
<div className="text-default text-sm">{t("select_date")}</div>
<DatePicker
isLoading={schedule.isLoading}
onChange={(date) => setSelectedDate(date ? date.format("YYYY-MM-DD") : date)}
onMonthChange={(date) => {
setMonth(date.format("YYYY-MM"));
setSelectedDate(date.format("YYYY-MM-DD"));
}}
includedDates={nonEmptyScheduleDays}
locale={i18n.language}
browsingDate={month ? dayjs(month) : undefined}
selected={dayjs(selectedDate)}
weekStart={weekdayToWeekIndex(event?.data?.users?.[0]?.weekStart)}
eventSlug={eventType?.slug}
/>
<Collapsible open>
<CollapsibleContent>
<div className="text-default text-sm">{t("select_date")}</div>
<DatePicker
isLoading={schedule.isLoading}
onChange={(date: Dayjs | null) => {
setSelectedDate(date === null ? date : date.format("YYYY-MM-DD"));
}}
onMonthChange={(date: Dayjs) => {
setMonth(date.format("YYYY-MM"));
setSelectedDate(date.format("YYYY-MM-DD"));
}}
includedDates={nonEmptyScheduleDays}
locale={i18n.language}
browsingDate={month ? dayjs(month) : undefined}
selected={dayjs(selectedDate)}
weekStart={weekdayToWeekIndex(event?.data?.users?.[0]?.weekStart)}
eventSlug={eventType?.slug}
/>
</CollapsibleContent>
</Collapsible>
</div>
{selectedDate ? (
<div className="mt-[9px] font-medium ">
@@ -51,11 +51,7 @@ const DateOverrideForm = ({
const [selectedDates, setSelectedDates] = useState<Dayjs[]>(value ? [dayjs.utc(value[0].start)] : []);
const onDateChange = (newDate: Dayjs | null) => {
// If no date is selected, do nothing
if (!newDate) {
return;
}
const onDateChange = (newDate: Dayjs) => {
// If clicking on a selected date unselect it
if (selectedDates.some((date) => yyyymmdd(date) === yyyymmdd(newDate))) {
setSelectedDates(selectedDates.filter((date) => yyyymmdd(date) !== yyyymmdd(newDate)));
@@ -154,7 +150,9 @@ const DateOverrideForm = ({
excludedDates={excludedDates}
weekStart={0}
selected={selectedDates}
onChange={(day) => onDateChange(day)}
onChange={(day) => {
if (day) onDateChange(day);
}}
onMonthChange={(newMonth) => {
setBrowsingDate(newMonth);
}}
+1 -1
View File
@@ -369,7 +369,7 @@ function UserDropdown({ small }: UserDropdownProps) {
<span
className={classNames(
small ? "h-4 w-4" : "h-5 w-5 ltr:mr-2 rtl:ml-2",
"relative flex-shrink-0 rounded-full bg-gray-300"
"relative flex-shrink-0 rounded-full "
)}>
<Avatar
size={small ? "xs" : "xsm"}
@@ -60,6 +60,7 @@ export function InviteMemberModal(props: Props) {
});
}}
teamId={orgId}
justEmailInvites={!!orgId}
isLoading={inviteMemberMutation.isLoading}
onSubmit={(values) => {
inviteMemberMutation.mutate({
@@ -24,11 +24,12 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
// A user can only have one org so we pass in their currentOrgId here
const currentOrgId = ctx.user?.organization?.id || input.orgId;
if (!currentOrgId || ctx.user.role !== UserPermissionRole.ADMIN)
throw new TRPCError({ code: "UNAUTHORIZED" });
const isUserOrganizationAdmin = currentOrgId && (await isOrganisationAdmin(ctx.user?.id, currentOrgId));
const isUserRoleAdmin = ctx.user.role === UserPermissionRole.ADMIN;
if (!(await isOrganisationAdmin(ctx.user?.id, currentOrgId)) || ctx.user.role !== UserPermissionRole.ADMIN)
throw new TRPCError({ code: "UNAUTHORIZED" });
const isUserAuthorizedToUpdate = !!(isUserOrganizationAdmin || isUserRoleAdmin);
if (!currentOrgId || !isUserAuthorizedToUpdate) throw new TRPCError({ code: "UNAUTHORIZED" });
if (input.slug) {
const userConflict = await prisma.team.findMany({
@@ -56,7 +56,7 @@ export const inviteMemberHandler = async ({ ctx, input }: InviteMemberOptions) =
});
const invitee = await getUserToInviteOrThrowIfExists({
usernameOrEmail,
orgId: input.teamId,
teamId: input.teamId,
isOrg: input.isOrg,
});
@@ -66,17 +66,21 @@ export async function getEmailsToInvite(usernameOrEmail: string | string[]) {
export async function getUserToInviteOrThrowIfExists({
usernameOrEmail,
orgId,
teamId,
isOrg,
}: {
usernameOrEmail: string;
orgId: number;
teamId: number;
isOrg?: boolean;
}) {
// Check if user exists in ORG or exists all together
const orgWhere = isOrg && {
organizationId: teamId,
};
const invitee = await prisma.user.findFirst({
where: {
OR: [{ username: usernameOrEmail, organizationId: orgId }, { email: usernameOrEmail }],
OR: [{ username: usernameOrEmail, ...orgWhere }, { email: usernameOrEmail }],
},
});
+1 -1
View File
@@ -40,7 +40,7 @@ export function Avatar(props: AvatarProps) {
let avatar = (
<AvatarPrimitive.Root
className={classNames(
"bg-emphasis item-center relative aspect-square justify-center overflow-hidden rounded-full",
"bg-emphasis item-center relative inline-flex aspect-square justify-center overflow-hidden rounded-full",
props.className,
sizesPropsBySize[size]
)}>
@@ -0,0 +1,361 @@
import { Canvas, Story, Meta } from "@storybook/addon-docs";
import { Button } from "@calcom/ui";
import {
Examples,
Example,
Title,
CustomArgsTable,
VariantRow,
VariantsTable,
} from "@calcom/storybook/components";
import { Plus, Trash, Copy } from "@calcom/ui/components/icon";
import {
Dropdown,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownItem,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "./Dropdown";
<Meta title="UI/Form/Dropdown" component={Dropdown} />
<Title title="Dropdown" suffix="Brief" subtitle="Version 1.0 — Last Update: 29 Aug 2023" />
## Definition
`Dropdown` is an element that displays a menu to the user—such as a set of actions or functions.
## Structure
The `Dropdown` component can be used to display a menu to the user.
<CustomArgsTable of={Dropdown} />
### Dropdown components that have arguments:
#### DropdownMenuTrigger
<CustomArgsTable of={DropdownMenuTrigger} />
#### DropdownMenuContent
<CustomArgsTable of={DropdownMenuContent} />
#### DropdownMenuItem
<CustomArgsTable of={DropdownMenuItem} />
#### DropdownMenuSeparator
<CustomArgsTable of={DropdownMenuSeparator} />
## Examples
<Examples title="Dropdown">
<Example title="Simple">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="No modal">
<Dropdown modal={false}>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
</Examples>
<Examples title="Dropdown Menu Trigger">
<Example title="Simple Button">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="Button Icon">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button StartIcon={Plus} />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="Disabled">
<Dropdown>
<DropdownMenuTrigger disabled={true} asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="Not as child">
<Dropdown>
<DropdownMenuTrigger>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
</Examples>
<Examples title="Dropdown Menu Content">
<Example title="Custom width">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent style={{ minWidth: "200px" }}>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="Align start">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent style={{ minWidth: "80px" }} align={"start"}>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="Align center">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent style={{ minWidth: "80px" }} align={"center"}>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="With Menu Separator">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="With side offset">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent sideOffset={50}>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
<Example title="With Some icons">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownItem StartIcon={Trash}>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem StartIcon={Plus}>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<DropdownItem StartIcon={Copy}>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
</Examples>
<Examples title="Dropdown Menu Label">
<Example title="Simple">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align={"start"}>
<DropdownMenuLabel>Number</DropdownMenuLabel>
<DropdownMenuItem>
<DropdownItem>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</Example>
</Examples>
## Dropdown Story
<Canvas>
<Story
name="Dropdown"
args={{
asChild: false,
align: "start",
disabled: false,
sideOffset: 0
}}
argTypes={{
sideOffset: {
control: {
type: "number",
},
},
asChild: {
control: {
type: "boolean"
}
},
align: {
control: {
type: "inline-radio",
options: ["center", "start", "end"],
},
},
disabled: {
control: {
type: "boolean"
}
}
}}>
{({ dir, asChild, sideOffset, align, disabled }) => (
<VariantsTable titles={["Default"]} columnMinWidth={150}>
<VariantRow>
<Dropdown>
<DropdownMenuTrigger disabled={disabled} asChild>
<Button>more</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align={align} sideOffset={sideOffset}>
<DropdownMenuItem>
<DropdownItem StartIcon={Trash}>First</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem StartIcon={Plus}>Second</DropdownItem>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownItem StartIcon={Copy}>Third</DropdownItem>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</VariantRow>
</VariantsTable>
)}
</Story>
</Canvas>