Skip to content
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
10 changes: 2 additions & 8 deletions components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { useState, useEffect, useRef } from 'react';
import { useState, useRef } from 'react';
import * as m from 'motion/react-m';
import { CtaButton } from '@/components/Cta';
import { Container } from '@/components/Container';
Expand Down Expand Up @@ -46,15 +46,9 @@ export const Accordion = ({
const font = isDigitalRed ? 'sans' : 'serif';
const fontWeight = isDigitalRed ? 'semibold' : 'bold';

const [openItems, setOpenItems] = useState<boolean[]>([]);
const [openItems, setOpenItems] = useState<boolean[]>(() => items?.map(item => item.defaultOpen) || []);
const firstItemRef = useRef<HTMLButtonElement>(null);

useEffect(() => {
if (!items?.length) return;
const initialState = items.map(item => item.defaultOpen);
setOpenItems(initialState);
}, [items]);

const toggleItem = (index: number) => {
setOpenItems(prevState => prevState.map((item, i) => i === index ? !item : item));
};
Expand Down
8 changes: 4 additions & 4 deletions components/Cta/CtaExternalLink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import React, { useEffect, useState } from 'react';
import React, { useMemo } from 'react';
import { cnb } from 'cnbuilder';
import { CtaContent } from './CtaContent';
import { type CtaCommonProps } from './Cta.types';
Expand Down Expand Up @@ -35,11 +35,11 @@ export const CtaExternalLink = React.forwardRef<HTMLAnchorElement, CtaExternalLi

// Add UTM params to Stanford URLs.
const { addUTMsToUrl } = useUTMs();
const [myHref, setMyHref] = useState<string>(href);
useEffect(() => {
const myHref = useMemo(() => {
if (isStanfordUrl(href)) {
setMyHref(addUTMsToUrl(href));
return addUTMsToUrl(href);
}
return href;
}, [href, addUTMsToUrl]);

return (
Expand Down
1 change: 1 addition & 0 deletions components/Search/Modal/SearchModalContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export function SearchModalProvider({
// Close the modal on route change
useEffect(() => {
if (pathname) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setIsOpen(false);
}
}, [pathname]);
Expand Down
2 changes: 1 addition & 1 deletion components/Search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const Search = ({

const searchClient = {
...algoliaClient,
// eslint-disable-next-line @typescript-eslint/no-explicit-any

search(requests: any[]) {
if (requests.every(({ params }) => !params?.query)) {
return Promise.resolve({
Expand Down
1 change: 1 addition & 0 deletions components/Storyblok/SbSupportPage/SbSupportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const SbSupportPage = ({ blok, slug }: SbSupportPageProps) => {
useEffect(() => {
const hash = window.location.hash.substring(1); // Remove the '#' symbol
if (hash && Object.keys(areasToSupport).includes(hash)) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setActiveFilter(hash as AreasToSupportType);
}
}, []);
Expand Down
32 changes: 17 additions & 15 deletions components/Storyblok/partials/SbLink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import React, { useEffect, useState } from 'react';
import React, { useMemo } from 'react';
import Link from 'next/link';
import { type SbLinkType } from '@/components/Storyblok/Storyblok.types';
import { getMaskedAsset } from '@/utilities/getMaskedAsset';
Expand Down Expand Up @@ -29,29 +29,31 @@ export const SbLink = React.forwardRef<HTMLAnchorElement, SbLinkProps>((props, r

// Storyblok link object either has a url (external links)
// or cached_url (internal or asset links)
let linkUrl = link.url || link.cached_url || '';
const rawLinkUrl = link.url || link.cached_url || '';
const isExternalLink = link.linktype === 'url';

// Ensure linkUrl is always a string and not undefined/null
if (typeof linkUrl !== 'string') {
linkUrl = '';
}
const baseLinkUrl = useMemo(() => {
if (typeof rawLinkUrl !== 'string') {
return '';
}
return rawLinkUrl;
}, [rawLinkUrl]);

const otherAttributes = attributes ?? {};

// State for external URL with UTMs
const [externalHref, setExternalHref] = useState<string>(linkUrl);

// Update external href when UTMs are available
useEffect(() => {
if (isExternalLink && isStanfordUrl(linkUrl)) {
setExternalHref(addUTMsToUrl(linkUrl));
// Compute external URL with UTMs
const externalHref = useMemo(() => {
if (isExternalLink && isStanfordUrl(baseLinkUrl)) {
return addUTMsToUrl(baseLinkUrl);
}
}, [isExternalLink, linkUrl, addUTMsToUrl]);
return baseLinkUrl;
}, [isExternalLink, baseLinkUrl, addUTMsToUrl]);

// Story or Internal type link.
// ---------------------------------------------------------------------------
if (link.linktype === 'story') {
let linkUrl = baseLinkUrl;
// Ensure internal links start with a slash for relative paths
if (!linkUrl.startsWith('/')) {
linkUrl = '/' + linkUrl;
Expand Down Expand Up @@ -100,7 +102,7 @@ export const SbLink = React.forwardRef<HTMLAnchorElement, SbLinkProps>((props, r
// ---------------------------------------------------------------------------
if (link.linktype === 'asset') {
// Rewrite the URL to the redirect link to mask the API endpoint.
linkUrl = getMaskedAsset(linkUrl);
const linkUrl = getMaskedAsset(baseLinkUrl);

return (
<a
Expand All @@ -118,7 +120,7 @@ export const SbLink = React.forwardRef<HTMLAnchorElement, SbLinkProps>((props, r
// Default if we don't know what type this is.
// ---------------------------------------------------------------------------
return (
<a ref={ref} href={linkUrl} className={className} {...otherAttributes}>
<a ref={ref} href={baseLinkUrl} className={className} {...otherAttributes}>
{children}
</a>
);
Expand Down
43 changes: 19 additions & 24 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { FlatCompat } from '@eslint/eslintrc';
import nextPlugin from 'eslint-config-next';
import stylistic from '@stylistic/eslint-plugin';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});

const eslintConfig = [{
ignores: ['node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts'],
}, ...compat.config({
extends: ['next/core-web-vitals', 'next/typescript'],
},
...nextPlugin,
{
files: ['**/*.{js,jsx,mjs,ts,tsx,mts,cts}'],
plugins: {
'@stylistic': stylistic,
},
rules: {
'comma-dangle': ['error', {
'objects': 'always-multiline',
Expand Down Expand Up @@ -69,25 +65,24 @@ const eslintConfig = [{
'import/no-unresolved': 0,
'no-unused-vars': 0,
'no-multi-spaces': 'error',
'import/export': 0,
'func-names': 0,
'semi': [1, 'always'], // 1 is for warning
'@next/next/no-img-element': 0,
// Stylistic
'@stylistic/no-trailing-spaces': 'error',
},
},
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@typescript-eslint/no-unused-vars': [
'warn',
{
'vars': 'local',
'args': 'none',
},
],
'import/export': 0,
'func-names': 0,
'semi': [1, 'always'], // 1 is for warning
'@next/next/no-img-element': 0,
},
}), {
plugins: {
'@stylistic': stylistic,
},
rules: {
// Stylistic
'@stylistic/no-trailing-spaces': 'error',
},
}];

Expand Down
Loading