;
+
+/**
+ * Union type that will only allow keys from types within the union thus giving bettern intellisense support
+ */
+export type InteractiveOrNonInteractiveElementProps =
+ | ButtonPropsWithoutAnchorProps
+ | AnchorPropsWithoutButtonProps
+ | DivPropsWithoutAnchorOrButtonProps;
diff --git a/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/index.ts b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/index.ts
new file mode 100644
index 00000000..99fc1c79
--- /dev/null
+++ b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/index.ts
@@ -0,0 +1,3 @@
+export { getRTLFlipOptOut } from './rtl-opt-out';
+export { warnDependent } from './warn-dependent';
+export { warnOutOfProd } from './warn-out-of-prod';
diff --git a/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/rtl-opt-out.ts b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/rtl-opt-out.ts
new file mode 100644
index 00000000..10882c04
--- /dev/null
+++ b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/rtl-opt-out.ts
@@ -0,0 +1,3 @@
+import { getRTL } from '@fluentui/react';
+
+export const getRTLFlipOptOut = (): string => (getRTL() ? '@noflip' : '');
diff --git a/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/warn-dependent.ts b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/warn-dependent.ts
new file mode 100644
index 00000000..21ecab29
--- /dev/null
+++ b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/warn-dependent.ts
@@ -0,0 +1,29 @@
+import type { ISettingsMap } from '@fluentui/react';
+import { warn } from '@fluentui/react';
+
+/**
+ * Warns when two props which are mutually exclusive are both being used.
+ *
+ * @public
+ * @param componentName - The name of the component being used.
+ * @param props - The props passed into the component.
+ * @param dependentMap - A map where the key is a dependent prop, and the value is the required prop.
+ */
+export function warnDependent(componentName: string, props: P, dependentMap: ISettingsMap
): void {
+ if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') {
+ for (const propName in dependentMap) {
+ if (props && propName in props) {
+ const propInDependentMapValue = dependentMap[propName];
+
+ // @ts-ignore propInDependentMapValue is a string
+ if (propInDependentMapValue && !(propInDependentMapValue in props)) {
+ warn(
+ `${componentName} property '${propName}' is no-op without '${
+ dependentMap[propName] ?? ''
+ }' also set.`,
+ );
+ }
+ }
+ }
+ }
+}
diff --git a/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/warn-out-of-prod.ts b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/warn-out-of-prod.ts
new file mode 100644
index 00000000..40e0ee3e
--- /dev/null
+++ b/SubwayNav/SubwayNav/utilities/MainUtilities/src/utilities/warn-out-of-prod.ts
@@ -0,0 +1,11 @@
+/**
+ * Throws a warning when not in production environments.
+ *
+ * @public
+ * @param message - Message to display.
+ */
+export function warnOutOfProd(message: string): void {
+ if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') {
+ console.warn(message);
+ }
+}
diff --git a/SubwayNav/SubwayNav/utilities/collapsible/src/collapsible/collapsible.base.tsx b/SubwayNav/SubwayNav/utilities/collapsible/src/collapsible/collapsible.base.tsx
new file mode 100644
index 00000000..2efb63de
--- /dev/null
+++ b/SubwayNav/SubwayNav/utilities/collapsible/src/collapsible/collapsible.base.tsx
@@ -0,0 +1,105 @@
+import type { IRenderFunction } from '@fluentui/react';
+import {
+ buttonProperties,
+ classNamesFunction,
+ getNativeProps,
+ Icon,
+} from '@fluentui/react';
+import { useSimpleId } from '../../../hooks/use-id/src/index';
+import { useControlledUncontrolled } from '../../../MainUtilities/src/index';
+import type { FunctionComponent, MouseEvent } from 'react';
+import * as React from 'react';
+
+import type {
+ ICollapsibleProps,
+ ICollapsibleStyle,
+ ICollapsibleStyleProps,
+} from './collapsible.types';
+
+const getClassNames = classNamesFunction();
+
+const onRenderTitleDefault: IRenderFunction = (props) => (
+ {props?.title}
+);
+
+export const CollapsibleBase: FunctionComponent = (props) => {
+ const {
+ ariaLabel,
+ children,
+ disabled = false,
+ iconProps,
+ isRequired = false,
+ onClick,
+ onRenderTitle = onRenderTitleDefault,
+ onToggle,
+ renderHeaderAs: HeaderAs = 'h2',
+ styles,
+ theme = {},
+ } = props;
+
+ // Expanding and collapsing logic.
+ const [isExpanded = false, setIsExpanded] = useControlledUncontrolled<
+ ICollapsibleProps,
+ boolean
+ props,
+ 'isExpanded',
+ 'defaultIsExpanded',
+ false,
+ );
+ const handleOnToggle = (e: MouseEvent) => {
+ const newIsExpanded = !isExpanded;
+
+ setIsExpanded(newIsExpanded);
+ onToggle?.(newIsExpanded);
+ onClick?.(e);
+ };
+
+ // Setting unique id’s to bind the content to the header.
+ const collapsibleId = useSimpleId();
+ const contentId = `collapsible-content_${collapsibleId}`;
+ const headerId = `collapsible-header_${collapsibleId}`;
+
+ /**
+ * Getting the native button props to spread into the internal button. Removing `title` from native props, because otherwise it would be applied to the button as native button attribute. If no text is available to the screen-reader inside the button, consumers should take advantage of the `ariaLabel` prop.
+ */
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const { title, ...nativeProps } = getNativeProps>(
+ props,
+ buttonProperties,
+ );
+
+ // Determining if a custom icon was passed in, and respect it.
+ const iconName = iconProps?.iconName ?? 'ChevronDownMed';
+
+ // Styles
+ const classNames = getClassNames(styles, { isExpanded, isRequired, disabled, theme });
+
+ return (
+
+
+