Skip to content

Commit

Permalink
follow a design system approach for components and values
Browse files Browse the repository at this point in the history
  • Loading branch information
odaridavid committed Mar 10, 2024
1 parent fcacfd2 commit 2697826
Show file tree
Hide file tree
Showing 19 changed files with 210 additions and 98 deletions.
78 changes: 67 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,21 @@ The folders are split into 4 boundaries:
the screen state a bit more predictable and it's easier to scan through what actions are possible
from a given screen.

The screen state e.g ```HomeScreenViewState``` is also modelled as a class with immutable
properties and makes state management way easier by reducing the state whenever their is a new
update received.
Some design patterns that can be seen here are the Observer pattern when consuming the flow ->
state flows in the composables and provides a reactive app.
The screen state e.g ```HomeScreenViewState``` is also modelled as a class with immutable
properties and makes state management way easier by reducing the state whenever their is a new
update received.
Some design patterns that can be seen here are the Observer pattern when consuming the flow ->
state flows in the composables and provides a reactive app.
</details>

![Add flow diagram here](/docs/MVI.png)

<details>
<summary>Testing</summary>

The data layer is unit tested by mocking out external dependencies and the ui layer on the
viewmodels, an integration test is written that makes use of fake,so as to mimic the real scenario
as much as possible over using mocks, which would also turn it to a unit test.
The data layer is unit tested by mocking out external dependencies and the ui layer on the
viewmodels, an integration test is written that makes use of fake,so as to mimic the real scenario
as much as possible over using mocks, which would also turn it to a unit test.
</details>

# Other Stuff 📦
Expand All @@ -119,9 +119,65 @@ dependency updates and running code quality checks ,defined in the `.github/work

*Design System*

```
TBD
```
Under the `designsystem` package ,it follows a tiered approach to styling the app i.e
<details>
<summary>Atoms (Smallest Components)</summary>
Typography:
Define font styles, sizes, and weights for headers, paragraphs, and other text elements.

Color Palette:
Establish a color palette with primary, secondary, and accent colors. Specify their usage in
different contexts.

Icons:
Design a set of basic icons that represent common actions or concepts. Ensure consistency in style
and sizing.

Buttons:
Create button styles with variations for primary, secondary, and tertiary actions. Include states
like hover and disabled.

Input Fields:
Design consistent styles for text inputs, checkboxes, radio buttons, and other form elements.
</details>
<details>
<summary>Molecules (Simple Components)</summary>
Form Elements:
Combine atoms to create complete form components. Ensure consistency in spacing and alignment.

Cards:
Combine text, images, and buttons to create card components. Define variations for different use cases.

Badges:
Assemble icons and text to create badge components for notifications or status indicators.

Avatars:
Design avatar components for user profiles, incorporating images or initials.
</details>
<details>
<summary>Organisms (Complex Components)</summary>
Navigation Bars:
Create a consistent navigation bar design that includes menus, icons, and navigation elements.

Headers and Footers:
Define headers and footers with appropriate spacing, logos, and navigation links.

Lists:
Assemble atoms and molecules to create list components, incorporating variations like simple lists,
detailed lists, and nested lists.

Modals:
Design modal components for overlays or pop-ups, ensuring consistency in styles and behavior.
</details>
<details>
<summary>Templates (Page-Level Structures)</summary>
Page Layouts:
Establish consistent layouts for different types of pages (e.g., home page, product page, settings
page).

Grid Systems:
Define grid systems that ensure alignment and consistency across various screen sizes.
</details>

*Performance*

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.core.content.ContextCompat
import com.github.odaridavid.weatherapp.designsystem.PermissionRationaleDialog
import com.github.odaridavid.weatherapp.designsystem.organism.PermissionRationaleDialog
import java.util.Locale

private const val NO_OF_ADDRESSES = 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package com.github.odaridavid.weatherapp.designsystem.theme
package com.github.odaridavid.weatherapp.designsystem

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import com.github.odaridavid.weatherapp.designsystem.atom.Dimensions
import com.github.odaridavid.weatherapp.designsystem.atom.LocalDimens
import com.github.odaridavid.weatherapp.designsystem.atom.LocalWeight
import com.github.odaridavid.weatherapp.designsystem.atom.Weight
import com.github.odaridavid.weatherapp.designsystem.atom.pink200
import com.github.odaridavid.weatherapp.designsystem.atom.pink500
import com.github.odaridavid.weatherapp.designsystem.atom.pink600
import com.github.odaridavid.weatherapp.designsystem.atom.pinkDarkPrimary
import com.github.odaridavid.weatherapp.designsystem.atom.shapes
import com.github.odaridavid.weatherapp.designsystem.atom.typography

private val LightColorPalette = lightColors(
primary = pink500,
Expand Down Expand Up @@ -36,3 +46,13 @@ fun WeatherAppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Compos
content = content
)
}

object WeatherAppTheme {
val dimens: Dimensions
@Composable
get() = LocalDimens.current

val weight: Weight
@Composable
get() = LocalWeight.current
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.odaridavid.weatherapp.designsystem.theme
package com.github.odaridavid.weatherapp.designsystem.atom

import androidx.compose.ui.graphics.Color

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.github.odaridavid.weatherapp.designsystem.atom

import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.dp

object Dimensions {
val none = 0.dp
val extraSmall = 4.dp
val small = 8.dp
val medium = 16.dp
val large = 24.dp
val extraLarge = 32.dp
}

object Weight{
val none = 0f
val half = 0.5f
val full = 1f
}

val LocalDimens = staticCompositionLocalOf { Dimensions }
val LocalWeight = staticCompositionLocalOf { Weight }
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.odaridavid.weatherapp.designsystem.theme
package com.github.odaridavid.weatherapp.designsystem.atom

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.odaridavid.weatherapp.designsystem.theme
package com.github.odaridavid.weatherapp.designsystem.atom

import androidx.compose.ui.text.TextStyle
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.github.odaridavid.weatherapp.designsystem
package com.github.odaridavid.weatherapp.designsystem.molecule

import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.selection.selectable
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
Expand All @@ -14,8 +12,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.github.odaridavid.weatherapp.R
import com.github.odaridavid.weatherapp.designsystem.WeatherAppTheme

@Composable
fun ConfirmButton(
Expand Down Expand Up @@ -44,7 +42,7 @@ fun SettingOptionRadioButton(
) {
Row(
Modifier
.padding(16.dp)
.padding(WeatherAppTheme.dimens.medium)
.selectable(
selected = (text == selectedOption),
onClick = { onOptionSelected(text) }
Expand All @@ -55,6 +53,6 @@ fun SettingOptionRadioButton(
selected = (text == selectedOption),
onClick = null
)
Body(text = text, modifier = Modifier.padding(start = 8.dp))
Body(text = text, modifier = Modifier.padding(start = WeatherAppTheme.dimens.small))
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.odaridavid.weatherapp.designsystem
package com.github.odaridavid.weatherapp.designsystem.molecule

import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
Expand All @@ -9,6 +9,9 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage

// todo check with 48
private val ICON_SIZE = 40.dp

@Composable
fun WeatherIcon(iconUrl: String, contentDescription: String, modifier: Modifier = Modifier) {
AsyncImage(
Expand All @@ -29,7 +32,7 @@ fun IconWithAction(
painter = painter,
contentDescription = contentDescription,
modifier = modifier
.defaultMinSize(40.dp)
.defaultMinSize(ICON_SIZE)
.clickable { onClicked() }
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.odaridavid.weatherapp.designsystem
package com.github.odaridavid.weatherapp.designsystem.molecule

import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
Expand All @@ -13,9 +13,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.github.odaridavid.weatherapp.R
import com.github.odaridavid.weatherapp.core.model.Temperature
import com.github.odaridavid.weatherapp.designsystem.WeatherAppTheme

@Composable
fun ErrorTextWithAction(
Expand All @@ -31,7 +30,7 @@ fun ErrorTextWithAction(
Button(
onClick = { onTryAgainClicked() },
modifier = Modifier
.padding(16.dp)
.padding(WeatherAppTheme.dimens.medium)
.align(Alignment.CenterHorizontally)
) {
Text(
Expand All @@ -44,7 +43,7 @@ fun ErrorTextWithAction(
}

@Composable
fun Headline(text: String, modifier: Modifier = Modifier, color: Color = Color.Unspecified) {
fun Headline(text: String, modifier: Modifier = Modifier) {
Text(
text = text,
modifier = modifier,
Expand All @@ -57,8 +56,8 @@ fun TemperatureHeadline(temperature: String, modifier: Modifier = Modifier) {
Headline(
text = temperature,
modifier = modifier
.padding(horizontal = 16.dp)
.padding(vertical = 8.dp)
.padding(horizontal = WeatherAppTheme.dimens.medium)
.padding(vertical = WeatherAppTheme.dimens.small)
)
}

Expand All @@ -67,7 +66,10 @@ fun Subtitle(text: String, modifier: Modifier = Modifier, color: Color = Color.U
Text(
text = text,
style = MaterialTheme.typography.subtitle1,
modifier = modifier.padding(horizontal = 16.dp, vertical = 8.dp),
modifier = modifier.padding(
horizontal = WeatherAppTheme.dimens.medium,
vertical = WeatherAppTheme.dimens.small
),
color = color
)
}
Expand Down Expand Up @@ -102,15 +104,15 @@ fun Body(text: String, modifier: Modifier = Modifier, color: Color = Color.Unspe
fun Temperature(text: String) {
Body(
text = text,
modifier = Modifier.padding(4.dp)
modifier = Modifier.padding(WeatherAppTheme.dimens.extraSmall)
)
}

@Composable
fun ForecastedTime(text: String, modifier: Modifier = Modifier) {
Body(
text = text,
modifier = modifier.padding(4.dp)
modifier = modifier.padding(WeatherAppTheme.dimens.extraSmall)
)
}

Expand All @@ -131,7 +133,7 @@ fun VersionInfoText(versionInfo: String, modifier: Modifier) {
text = versionInfo,
modifier = modifier
.fillMaxWidth()
.padding(16.dp),
.padding(WeatherAppTheme.dimens.medium),
textAlign = TextAlign.Center
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.odaridavid.weatherapp.designsystem
package com.github.odaridavid.weatherapp.designsystem.organism

import android.Manifest
import androidx.activity.result.ActivityResultLauncher
Expand All @@ -25,6 +25,13 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.constraintlayout.compose.ConstraintLayout
import com.github.odaridavid.weatherapp.R
import com.github.odaridavid.weatherapp.designsystem.WeatherAppTheme
import com.github.odaridavid.weatherapp.designsystem.molecule.ConfirmButton
import com.github.odaridavid.weatherapp.designsystem.molecule.SettingOptionRadioButton

// TODO Adapt per screen
private val SETTING_DIALOG_MAX_HEIGHT = 200.dp
private val SETTING_DIALOG_MIN_HEIGHT = 0.dp

@Composable
fun PermissionRationaleDialog(
Expand Down Expand Up @@ -80,7 +87,7 @@ fun <T> SettingOptionsDialog(
val (dialog, confirmButton) = createRefs()
LazyColumn(
modifier = Modifier
.heightIn(0.dp, 200.dp)
.heightIn(SETTING_DIALOG_MIN_HEIGHT, SETTING_DIALOG_MAX_HEIGHT)
.fillMaxWidth()
.constrainAs(dialog) {
top.linkTo(parent.top, margin = 16.dp)
Expand All @@ -106,7 +113,7 @@ fun <T> SettingOptionsDialog(
Spacer(modifier = Modifier.weight(1f))
ConfirmButton(
modifier = Modifier
.padding(16.dp),
.padding(WeatherAppTheme.dimens.medium),
onClick = {
onConfirm()
}
Expand Down
Loading

0 comments on commit 2697826

Please sign in to comment.