Injecting code into UT Pages - Longhorn-Developers/UT-Registration-Plus GitHub Wiki

Code Injection Guide

This page documents how we inject code into supported pages across the UT Austin web ecosystem.

Choosing a page to inject on

Current URL is obtained through window.location.href by calling getSiteSupport

siteSupport

Contains enum to determine what page a user is on. For example

if (url.includes('my.utexas.edu/student/student/index') || url.includes('my.utexas.edu/student/')) {
    return SiteSupport.MY_UT;
}

renderComponent

Found in index.tsx in pages, the renderComponent function is a utility used by the extension to inject React components into websites (and other supported pages). It creates a new div and appends it to the document body. It then uses React’s createRoot to render the React component inside this container.

const renderComponent = (Component: React.ComponentType) => {
    const container = document.createElement('div');
    container.id = 'extension-root';
    document.body.appendChild(container);

    createRoot(container).render(
        <React.StrictMode>
            <Component />
        </React.StrictMode>
    );
};

After a site is determined, components are rendered in index.tsx. For example

if (support === SiteSupport.COURSE_CATALOG_DETAILS || support === SiteSupport.COURSE_CATALOG_LIST) {
    renderComponent(() => <CourseCatalogMain support={support} />);
}

if (support === SiteSupport.MY_UT) {
    renderComponent(InjectedButton);
}

if (support === SiteSupport.COURSE_CATALOG_SEARCH) {
    renderComponent(DaysCheckbox);
}

Erasing elements on webpages

This can be accomplished easily through retrieving elements, for example using document.getElementById, and changing their innerHTML to ''

For example with DaysCheckbox

        const daysDropdown = document.getElementById('mtg_days_st') as HTMLSelectElement | null;
        if (!daysDropdown) {
            console.error('Days dropdown not found');
            return;
        }

        const formElement = daysDropdown.closest('.form_element')!;
        const checkboxContainer = document.createElement('div');

        // Create a hidden input to store the value
        const hiddenInput = document.createElement('input');
        hiddenInput.type = 'hidden';
        hiddenInput.name = 'mtg_days_st';
        hiddenInput.id = 'mtg_days_st_hidden';
        hiddenInput.value = daysDropdown.value;

        // Remove old dropdown
        formElement.innerHTML = '';

Examples

CourseCatalogMain

Relevant page: image

<ExtensionRoot> used to prevent the native page styles from being overridden with our own.

return (
        <ExtensionRoot>
            <NewSearchLink />
            <RecruitmentBanner />
            <TableHead>Plus</TableHead>
            {rows.map(
                row =>
                    row.course && (
                        <TableRow
                            key={row.course.uniqueId}
                            row={row}
                            isSelected={row.course.uniqueId === selectedCourse?.uniqueId}
                            activeSchedule={activeSchedule}
                            onClick={handleRowButtonClick(row.course)}
                        />
                    )
            )}
            <CourseCatalogInjectedPopup
                course={selectedCourse!} // always defined when showPopup is true
                show={showPopup}
                onClose={() => setShowPopup(false)}
                afterLeave={() => setSelectedCourse(null)}
            />
            {enableScrollToLoad && <AutoLoad addRows={addRows} />}
        </ExtensionRoot>
    );

AddAllButton

// Existing code above
    return ReactDOM.createPortal(
        <ExtensionRoot>
            <Button variant='filled' color='ut-burntorange' onClick={extractCoursesFromCalendar}>
                Add Courses to UT Registration+
            </Button>
        </ExtensionRoot>,
        container
    );
⚠️ **GitHub.com Fallback** ⚠️