Merge branch 'master' into feat/studio-logs-page-query-params

This commit is contained in:
TzeYiing committed 2022-01-03 16:17:50 +08:00
commit 73f22ee2d4
14 files changed
+199 -43

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Studio Unit Tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
paths:
- 'studio/**'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install deps
run: npm i
working-directory: ./studio
- name: Run tests
run: npm test
working-directory: ./studio
@@ -5,6 +5,7 @@ import DataGrid from '@supabase/react-data-grid'
import LogSelection from './LogSelection'
import { LogData } from './Logs.types'
import { isNil } from 'lodash'
interface Props {
isCustomQuery: boolean
@@ -50,12 +51,13 @@ const LogTable = ({ isCustomQuery, data }: Props) => {
}, {})
}, [JSON.stringify(data)])
const strLogMap = JSON.stringify(logMap)
useEffect(() => {
if (!data) return
if (isNil(data)) return
if (focusedLog && !(focusedLog.id in logMap)) {
setFocusedLog(null)
}
}, [Object.keys(logMap)])
}, [strLogMap])
if (!data) return null
@@ -64,7 +66,7 @@ const LogTable = ({ isCustomQuery, data }: Props) => {
const logDataRows = useMemo(() => {
return Object.values(logMap).sort((a, b) => a.timestamp - b.timestamp)
}, [JSON.stringify(Object.keys(logMap))])
}, [strLogMap])
return (
<section className="flex flex-1 flex-row" style={{ maxHeight }}>
<DataGrid
@@ -3,7 +3,7 @@ import Link from 'next/link'
import { observer } from 'mobx-react-lite'
import { useRouter } from 'next/router'
import { isUndefined } from 'lodash'
import { Button, Divider, IconHome, Dropdown, IconUser, Toggle, Typography } from '@supabase/ui'
import { Button, Divider, IconHome, Dropdown, IconUser, Select, Typography } from '@supabase/ui'
import { IS_PLATFORM } from 'lib/constants'
import { useStore } from 'hooks'
@@ -77,13 +77,16 @@ const NavigationBar: FC<Props> = ({}) => {
: []),
<Divider key="d1" light />,
<Dropdown.Misc key="theme">
<div className="w-[200px] pt-1">
<Toggle
size="small"
label="Enable dark mode"
checked={ui.isDarkTheme}
onChange={() => ui.toggleTheme()}
/>
<div className="w-[240px] py-1 flex items-center justify-between">
<Typography.Text>Theme</Typography.Text>
<Select
value={ui.themeOption}
onChange={(e: any) => ui.onThemeOptionChange(e.target.value)}
>
<Select.Option value="system">System default</Select.Option>
<Select.Option value="dark">Dark</Select.Option>
<Select.Option value="light">Light</Select.Option>
</Select>
</div>
</Dropdown.Misc>,
]}
+11 -1
View File
@@ -1,6 +1,6 @@
import { useMonaco } from '@monaco-editor/react'
import { autorun } from 'mobx'
import { createContext, FC, useContext, useEffect } from 'react'
import { createContext, FC, useContext, useEffect, useCallback } from 'react'
import toast from 'react-hot-toast'
import { IRootStore } from 'stores'
@@ -33,8 +33,14 @@ export const StoreProvider: FC<StoreProvider> = ({ children, rootStore }) => {
}
}, [theme, monaco])
const matchMediaEvent = useCallback(() => {
ui.themeOption === 'system' &&
ui.setTheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
}, [])
useEffect(() => {
ui.load()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', matchMediaEvent)
autorun(() => {
if (ui.notification) {
@@ -71,6 +77,10 @@ export const StoreProvider: FC<StoreProvider> = ({ children, rootStore }) => {
}
}
})
return () =>
window
.matchMedia('(prefers-color-scheme: dark)')
.removeEventListener('change', matchMediaEvent)
}, [])
return <StoreContext.Provider value={rootStore}>{children}</StoreContext.Provider>
+13 -16
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React from 'react'
import { observer } from 'mobx-react-lite'
import { IconMoon, IconSun, Select, Typography } from '@supabase/ui'
@@ -77,15 +77,9 @@ const ProfileCard = observer(() => {
)
})
function ThemeSettings() {
const [theme, setTheme] = useState('')
const ThemeSettings = observer(() => {
const { ui } = useStore()
useEffect(() => {
const localStorageTheme = window.localStorage.getItem('theme')
if (localStorageTheme) setTheme(localStorageTheme)
}, [])
return (
<Panel
title={[
@@ -96,22 +90,25 @@ function ThemeSettings() {
>
<Panel.Content>
<Select
value={theme}
value={ui.themeOption}
label="Interface theme"
descriptionText="Choose a theme preference"
layout="horizontal"
style={{ width: '50%' }}
icon={theme === 'light' ? <IconSun /> : <IconMoon />}
onChange={(e: any) => {
setTheme(e.target.value)
ui.setTheme(e.target.value)
}}
icon={
ui.themeOption === 'light' ? (
<IconSun />
) : ui.themeOption === 'dark' ? (
<IconMoon />
) : undefined
}
onChange={(e: any) => ui.onThemeOptionChange(e.target.value)}
>
{/* <Select.Option value="system">System default</Select.Option> */}
<Select.Option value="system">System default</Select.Option>
<Select.Option value="dark">Dark</Select.Option>
<Select.Option value="light">Light</Select.Option>
</Select>
</Panel.Content>
</Panel>
)
}
})
@@ -131,7 +131,6 @@ export const LogPage: NextPage = () => {
setLatestRefresh(new Date().toISOString())
setParams({ ...params, timestamp_start: '' })
setSize(1)
mutate()
}
const handleModeToggle = () => {
+28 -11
View File
@@ -7,6 +7,7 @@ import Telemetry from 'lib/telemetry'
export interface IUiStore {
language: 'en_US'
theme: 'dark' | 'light'
themeOption: 'dark' | 'light' | 'system'
isDarkTheme: boolean
selectedProject?: Project
@@ -15,8 +16,8 @@ export interface IUiStore {
profile?: User
load: () => void
toggleTheme: () => void
setTheme: (theme: 'dark' | 'light') => void
onThemeOptionChange: (themeOption: 'dark' | 'light' | 'system') => void
setProjectRef: (ref?: string) => void
setOrganizationSlug: (slug?: string) => void
setNotification: (notification: Notification) => string
@@ -26,6 +27,7 @@ export default class UiStore implements IUiStore {
rootStore: IRootStore
language: 'en_US' = 'en_US'
theme: 'dark' | 'light' = 'dark'
themeOption: 'dark' | 'light' | 'system' = 'dark'
selectedProjectRef?: string
selectedOrganizationSlug?: string
@@ -73,24 +75,39 @@ export default class UiStore implements IUiStore {
load() {
if (typeof window === 'undefined') return
const savedTheme = (window.localStorage.getItem('theme') ?? 'dark') as 'dark' | 'light'
this.setTheme(savedTheme)
}
toggleTheme() {
if (this.theme === 'dark') {
this.setTheme('light')
} else {
this.setTheme('dark')
const localStorageThemeOption = window.localStorage.getItem('theme')
if (localStorageThemeOption === 'system') {
this.themeOption = localStorageThemeOption
return this.setTheme(
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
)
}
if (localStorageThemeOption === 'light') {
this.themeOption = localStorageThemeOption
return this.setTheme('light')
}
window.localStorage.setItem('theme', 'dark')
this.themeOption = 'dark'
this.setTheme('dark')
}
setTheme(theme: 'dark' | 'light') {
this.theme = theme
window.localStorage.setItem('theme', theme)
document.body.className = theme
}
onThemeOptionChange(themeOption: 'dark' | 'light' | 'system') {
this.themeOption = themeOption
if (themeOption === 'system') {
window.localStorage.setItem('theme', 'system')
return this.setTheme(
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
)
}
window.localStorage.setItem('theme', themeOption)
this.setTheme(themeOption)
}
setProjectRef(ref?: string) {
this.selectedProjectRef = ref
}
+1 -1
View File
@@ -100,7 +100,7 @@ test('Refreshpage', async () => {
await waitFor(() => screen.getByText(/my_key/))
// simulate refresh
await waitFor(() => userEvent.click(screen.getByText(/Refresh/)))
userEvent.click(screen.getByText(/Refresh/))
// when log line unmounts and it was focused, should close focus panel
await waitFor(() => screen.queryByText(/my_key/) === null, { timeout: 1000 })
await waitFor(() => screen.queryByText(/happened/) === null, { timeout: 1000 })
+92
View File
@@ -0,0 +1,92 @@
---
id: auth-azure
title: 'Login with Azure'
description: Add Azure OAuth to your Supabase project
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
To enable Azure Auth for your project, you need to set up an Azure OAuth application and add the application credentials to your Supabase Dashboard.
## Overview
Azure OAuth consists of six broad steps:
- Create an application under Azure Active Directory.
- Obtain a `Application (client) ID` with “Sign In with Azure” capabilities. This will be used as the `client id`.
- Create a `Secret ID` with “Sign In with Azure” capabilities. The value of the secret will be used as the `client secret`.
- Whitelist the callback url of your application.
## Steps
### Access your Azure Developer account
- Go to [portal.azure.com](https://portal.azure.com/#home).
- Login and select "Azure Active Directory" under the list of Azure Services.
### Register an application
- Under Azure Active Directory, select "App registrations" in the side panel.
- Select "New registration".
- Choose a name and select your preferred option for the supported account types.
- Specify the "Redirect URI".
- The redirect / callback URI should look like this: `https://<project-ref>.supabase.co/auth/v1/callback`
- Click "Register" at the bottom of the form.
![Register an application.](/img/guides/azure-register-app.png)
### Obtain a Client ID
This will serve as the `client_id` when you make API calls to authenticate the user.
- Once your app has been registered, the client id can be found under the [list of app registrations](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) under the column titled "Application (client) ID".
![Obtain the client id](/img/guides/azure-client-id.png)
### Obtain a Secret ID
This will serve as the `client_secret` when you make API calls to authenticate the user.
- Click on the name of the app registered above.
- Under "Essentials", click on "Client credentials".
- Navigate to the "Client secrets" tab and select "New client secret".
- Enter a description and choose your preferred expiry for the secret.
- Once the secret is generated, save the value (not the secret ID).
![Obtain the client secret](/img/guides/azure-client-secret.png)
### Add login code to your client app
The JavaScript client code is documented in the [Supabase OAuth Reference](/docs/reference/javascript/auth-signin#sign-in-using-third-party-providers).
```js
const { user, session, error } = await supabase.auth.signIn({
provider: 'Azure',
})
```
Add a function which you can call from a button, link, or UI element.
```js
async function signInWithAzure() {
const { user, session, error } = await supabase.auth.signIn({
provider: 'Azure',
}, {
scopes: 'email',
})
}
```
To log out:
```js
async function signout() {
const { error } = await supabase.auth.signOut()
}
```
## Resources
- [Azure Developer Account](https://portal.azure.com).
- [Github Discussion](https://github.com/supabase/gotrue/pull/54#issuecomment-757043573).
+1
View File
@@ -148,6 +148,7 @@ module.exports = {
'guides/auth/auth-email',
'guides/auth/auth-magic-link',
'guides/auth/auth-apple',
'guides/auth/auth-azure',
'guides/auth/auth-bitbucket',
'guides/auth/auth-discord',
'guides/auth/auth-facebook',
+2 -2
View File
@@ -29,10 +29,10 @@ const authProviders = [
{
name: 'Azure',
// logo: '/img/libraries/dart-icon.svg',
href: 'https://github.com/supabase/gotrue/pull/54#issuecomment-757043573',
href: '/docs/guides/auth/auth-azure',
official: false,
supporter: 'TBD',
platform: false,
platform: true,
selfHosted: true,
},
{
Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB